<commit_before>#!/usr/bin/env python3

from datetime import datetime
from impala.catalog import models
from impala import db
from impala.api.v1 import bp
from flask_restful import Api, Resource, abort, reqparse
from flask import make_response, json
import sqlalchemy
from uuid import uuid4


class ApiVersionInfo(Resource):
    # No auth required for this endpoint
    def get(self):
        return {'stable': False}


def all_fields(model):
    columns = [c.name for c in model.__table__.columns]
    return dict([(c, getattr(model, c)) for c in columns])


class ImpalaResource(Resource):
    # TODO require auth
    def get(self, model, id=None):
        if id:
            item = model.query.get(id)
            if not item:
                abort(404, success=False, message="Item not found")
            return all_fields(item)
        else:
            items = model.query.all()
            return [all_fields(item) for item in items]

    def put(self, model, added_by="Unknown"):
        post_parser = reqparse.RequestParser()

        for c in model.__table__.columns:
            name = c.name
            required = not c.nullable
            if name not in ['id', 'added_at', 'added_by']:
                post_parser.add_argument(name, dest=name, required=required)
            else:
                post_parser.add_argument(name, dest=name, required=False)

        args = post_parser.parse_args()
        if not args['id']:
            args['id'] = uuid4().__str__()

        # TODO auth - require user to have impersonation permissions
        if not args['added_by']:
            args['added_by'] = added_by
        if not args['added_at']:
            args['added_at'] = datetime.now()

        del_list = []
        for k, v in args.items():
            if not v:
                del_list.append(k)
        for k in del_list:
            args.pop(k)

        item = model(**args)
        try:
            db.session.add(item)
            db.session.commit()
            return "Item added", 200

        except sqlalchemy.exc.IntegrityError:
            abort(409, success=False, message="Item already exists or foreign key constraint not met")
        except sqlalchemy.exc.StatementError:
            abort(400, success=False, message="Invalid parameter syntax")

    def patch(self, model, id):
        post_parser = reqparse.RequestParser()

        for c in model.__table__.columns:
            name = c.name
            if name not in ['id', 'added_at', 'added_by']:
                post_parser.add_argument(name, dest=name, required=False)

        args = post_parser.parse_args()

        del_list = []
        for k, v in args.items():
            if not v:
                del_list.append(k)
        for k in del_list:
            args.pop(k)

        try:
            model.query.filter_by(id=id).update(args)
            db.session.commit()
            return "Item updated", 200

        except sqlalchemy.exc.IntegrityError:
            abort(409, success=False, message="Invalid change")
        except sqlalchemy.exc.StatementError:
            abort(400, success=False, message="Invalid parameter syntax")


class Stack(ImpalaResource):
    def get(self, id):
        return super().get(models.Stack, id)

    def patch(self, id):
        return super().patch(models.Stack, id)


class StackList(ImpalaResource):
    def get(self):
        return super().get(models.Stack)

    def put(self):
        return super().put(models.Stack)

<commit_msg>Rollback DB changes on sqlalchemy exceptions<commit_after>#!/usr/bin/env python3

from datetime import datetime
from impala.catalog import models
from impala import db
from impala.api.v1 import bp
from flask_restful import Api, Resource, abort, reqparse
from flask import make_response, json
import sqlalchemy
from uuid import uuid4


class ApiVersionInfo(Resource):
    # No auth required for this endpoint
    def get(self):
        return {'stable': False}


def all_fields(model):
    columns = [c.name for c in model.__table__.columns]
    return dict([(c, getattr(model, c)) for c in columns])


class ImpalaResource(Resource):
    # TODO require auth
    def get(self, model, id=None):
        if id:
            item = model.query.get(id)
            if not item:
                abort(404, success=False, message="Item not found")
            return all_fields(item)
        else:
            items = model.query.all()
            return [all_fields(item) for item in items]

    def put(self, model, added_by="Unknown"):
        post_parser = reqparse.RequestParser()

        for c in model.__table__.columns:
            name = c.name
            required = not c.nullable
            if name not in ['id', 'added_at', 'added_by']:
                post_parser.add_argument(name, dest=name, required=required)
            else:
                post_parser.add_argument(name, dest=name, required=False)

        args = post_parser.parse_args()
        if not args['id']:
            args['id'] = uuid4().__str__()

        # TODO auth - require user to have impersonation permissions
        if not args['added_by']:
            args['added_by'] = added_by
        if not args['added_at']:
            args['added_at'] = datetime.now()

        del_list = []
        for k, v in args.items():
            if not v:
                del_list.append(k)
        for k in del_list:
            args.pop(k)

        item = model(**args)
        try:
            db.session.add(item)
            db.session.commit()
            return "Item added", 200

        except sqlalchemy.exc.IntegrityError:
            db.session.rollback()
            abort(409, success=False, message="Item already exists or foreign key constraint not met")
        except sqlalchemy.exc.StatementError:
            db.session.rollback()
            abort(400, success=False, message="Invalid parameter syntax")
        except:
            db.session.rollback()
            abort(500, success=False, message="Something broke during the query")

    def patch(self, model, id):
        post_parser = reqparse.RequestParser()

        for c in model.__table__.columns:
            name = c.name
            if name not in ['id', 'added_at', 'added_by']:
                post_parser.add_argument(name, dest=name, required=False)

        args = post_parser.parse_args()

        del_list = []
        for k, v in args.items():
            if not v:
                del_list.append(k)
        for k in del_list:
            args.pop(k)

        try:
            model.query.filter_by(id=id).update(args)
            db.session.commit()
            return "Item updated", 200

        except sqlalchemy.exc.IntegrityError:
            db.session.rollback()
            abort(409, success=False, message="Invalid change")
        except sqlalchemy.exc.StatementError:
            db.session.rollback()
            abort(400, success=False, message="Invalid parameter syntax")
        except:
            db.session.rollback()
            abort(500, success=False, message="Something broke during the query")


class Stack(ImpalaResource):
    def get(self, id):
        return super().get(models.Stack, id)

    def patch(self, id):
        return super().patch(models.Stack, id)


class StackList(ImpalaResource):
    def get(self):
        return super().get(models.Stack)

    def put(self):
        return super().put(models.Stack)


{'fraction_non_alphanumeric': 0.10385395537525355, 'fraction_numerical': 0.007572684246112238, 'mean_word_length': 2.123310810810811, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1642824', 'n_tokens_mistral': 2158, 'n_tokens_neox': 2073, 'n_words': 583}