def submit_document_external(): knows_what_they_are_doing = get_user() and get_user().has_permission( 'info_protokolle', 'info_klausuren', 'mathe_protokolle', 'mathe_klausuren') submit_documents(validated=bool(knows_what_they_are_doing))
def submit_document_external(): knows_what_they_are_doing = get_user() and get_user().has_permission( 'info_protokolle', 'info_klausuren', 'mathe_protokolle', 'mathe_klausuren') return submit_documents(validated=bool(knows_what_they_are_doing))
def submit_documents(validated): """Student document submission endpoint POST data must be multipart, with the `json` part conforming to DocumentLoadSchema and the `file` part being a pdf file. Uploaded files are stored in subdirectories below config.DOCUMENT_DIRECTORY. This method may raise AssertionErrors when the user sends invalid data. """ # we can't use @deserialize because this endpoint uses multipart form data data = json.loads(request.form['json']) if get_user(): (data, errors) = FullDocumentLoadSchema().load(data) if (data.get('document_type') == 'written' and data.get('solution') not in ['official', 'inofficial', 'none'] or data.get('document_type') != 'written' and data.get('solution') is not None): errors['solution'] = 'Invalid value.' else: (data, errors) = DocumentLoadSchema().load(data) if errors: raise ClientError(*errors) assert 'file' in request.files file = request.files['file'] if not _allowed_file(file.filename): raise ClientError('file extension not allowed', status=406) lectures = _match_lectures(data['lectures'], validated) examinants = _match_examinants(data['examinants'], validated) date = data['date'] if not get_user(): assert date <= datetime.date.today() new_doc = Document( department=data['department'], lectures=lectures, examinants=examinants, date=date, number_of_pages=0, # will be filled in later or upon validation document_type=data['document_type'], validated=validated, validation_time=datetime.datetime.now() if validated else None, comment=data.get('comment'), solution=data.get('solution'), submitted_by=data.get('student_name')) sqla.session.add(new_doc) # we have the db side of things taken care of, now save the file # and tell the db where to find the file sqla.session.flush() # necessary for id field to be populated save_file(new_doc, file) if validated: # we don't trust other people's PDFs... new_doc.number_of_pages = number_of_pages(new_doc) sqla.session.commit() app.logger.info("New document submitted (id: {})".format(new_doc.id)) if validated: config.document_validated(document_path(new_doc.id)) return {}
class DocumentLoadSchema(Schema): # used by document submission department = fields.Str(required=True, validate=OneOf(['computer science', 'mathematics', 'other'])) lectures = fields.List(fields.Str(), required=True) examinants = fields.List(fields.Str(), required=True) date = fields.Date(required=True) document_type = fields.Str(required=True, validate=lambda x: get_user() and x == 'written' or x in ['oral', 'oral reexam', 'mock exam']) student_name = fields.Str(required=False, allow_none=True)
def view_document(instance_id): if get_user() or is_kiosk(): doc = Document.query.get(instance_id) if doc is None or not doc.has_file: raise ClientError('document not found', status=404) return send_file(document_path(doc.id)) else: return unauthorized()
def view_document(instance_id): if get_user() or is_kiosk(): doc = Document.query.get(instance_id) if doc is None or not doc.has_file: raise ClientError('document not found', status=404) return send_file(document_path(doc.id)) else: return unauthorized()
def view_document(instance_id): doc = Document.query.get(instance_id) if get_user() or is_kiosk() or doc.publicly_available: if doc is None or not doc.has_file: raise ClientError('document not found', status=404) return send_file(document_path(doc.id), as_attachment=(request.args.get('download') is not None)) else: return unauthorized()
def handle_get(instance_id=None): if instance_id is None: # GET MANY assert 'GET' in schemas, "GET schema missing" schema = schemas['GET'] allow_insecure = allow_insecure_authenticated and get_user() return filtered_results(query_fn(), schema, paginate_many, allow_insecure=allow_insecure) else: # GET SINGLE result = query_fn().get(instance_id) obj = serialize(result, schema) return obj
def log_deposit_return(data): if 'document_id' in data: doc = Document.query.get(data['document_id']) # data privacy, yo doc.submitted_by = None dep = Deposit.query.get(data['id']) sqla.session.delete(dep) db.accounting.log_deposit_return(dep, get_user(), data['cash_box']) sqla.session.commit() return {}
def log_deposit_return(data): if 'document_id' in data: doc = Document.query.get(data['document_id']) # data privacy, yo doc.submitted_by = None dep = Deposit.query.get(data['id']) if Deposit.query.filter(Deposit.id == data['id']).delete() == 0: raise ClientError('deposit not found') db.accounting.log_deposit_return(dep, get_user(), data['cash_box']) sqla.session.commit() return {}
def scanner(location, id): assert location in config.FS_CONFIG['OFFICES'] assert 0 <= id <= len(config.LASER_SCANNERS[location]) (host, port) = config.LASER_SCANNERS[location][id] bs = barcode.BarcodeScanner(host, port, get_user().first_name) yield None # exit application context, start event stream for doc in bs: if doc is None: # socket read has timeouted, try to write to output stream yield (None, None) else: yield (None, serialize(doc, DocumentDumpSchema))
def scanner(location, id): assert location in config.FS_CONFIG['OFFICES'] assert 0 <= id <= len(config.LASER_SCANNERS[location]) (host, port) = config.LASER_SCANNERS[location][id] bs = barcode.BarcodeScanner(host, port, get_user().first_name) schema = DocumentDumpSchema() # __init__ requires application context yield None # exit application context, start event stream for doc in bs: if doc is None: # socket read has timeouted, try to write to output stream yield (None, None) else: yield (None, serialize(doc, lambda: schema))
def jsonify(*args, **kwargs): # http://flask.pocoo.org/snippets/45/ def request_wants_json(): best = request.accept_mimetypes \ .best_match(['application/json', 'text/html']) return best == 'application/json' and \ request.accept_mimetypes[best] > \ request.accept_mimetypes['text/html'] data = json.dumps(dict(*args, **kwargs), indent=None if request.is_xhr else 2) if not get_user() or request_wants_json(): return Response(data, mimetype='application/json') else: # provide some HTML for flask-debugtoolbar to inject into return '<html><body><pre>{}</pre></body></html>'.format(data)
def jsonify(*args, **kwargs): # http://flask.pocoo.org/snippets/45/ def request_wants_json(): best = request.accept_mimetypes \ .best_match(['application/json', 'text/html']) return best == 'application/json' and \ request.accept_mimetypes[best] > \ request.accept_mimetypes['text/html'] data = json.dumps(dict(*args, **kwargs)) if not get_user() or request_wants_json(): return Response(data, mimetype='application/json') else: # provide some HTML for flask-debugtoolbar to inject into return '<html><body><pre>{}</pre></body></html>'.format(data)
def log_early_document_reward(data): doc = Document.query.get(data['id']) if doc is None: raise ClientError('document not found') if not doc.early_document_eligible: raise ClientError('document not eligible for early document reward') doc.early_document_eligible = False # data privacy, yo if not doc.deposit_return_eligible: doc.submitted_by = None db.accounting.log_early_document_disburse(get_user(), data['cash_box']) sqla.session.commit() return {'disbursal': config.FS_CONFIG['EARLY_DOCUMENT_REWARD']}
def log_early_document_reward(data): doc = Document.query.get(data['id']) if doc is None: raise ClientError('document not found') if not doc.early_document_eligible: raise ClientError('document not eligible for early document reward') doc.early_document_eligible = False # data privacy, yo if not doc.deposit_return_eligible: doc.submitted_by = None db.accounting.log_early_document_disburse(get_user(), data['cash_box']) sqla.session.commit() return {'disbursal': config.FS_CONFIG['EARLY_DOCUMENT_REWARD']}
def merge(self, ids): lectures = Lecture.query.filter(Lecture.id.in_(ids)).all() first = next(lecture for lecture in lectures if lecture.id == int(ids[0])) all_docs = set() for lecture in lectures: all_docs |= set(lecture.documents) if lecture != first: sqla.session.delete(lecture) first.documents = list(all_docs) sqla.session.commit() flash('Vorlesungen in "{}" zusammengefügt'.format(first.name)) config.log_admin_audit(self, first, '\n\n'.join(['Lectures merged by {}'.format(get_user().full_name), '{}->{}'.format([l.name for l in lectures], first.name), url_for('lecture.edit_view', id=first.id, _external=True)]))
def log_deposit_return(data): if 'document_id' in data: doc = Document.query.get(data['document_id']) if doc is None: raise ClientError('document not found') doc.deposit_return_eligible = False # data privacy, yo if not doc.early_document_eligible: doc.submitted_by = None dep = Deposit.query.get(data['id']) if Deposit.query.filter(Deposit.id == data['id']).delete() == 0: raise ClientError('deposit not found') db.accounting.log_deposit_return(dep, get_user(), data['cash_box']) sqla.session.commit() return {}
def _log_model_changes(self, model, state): if state == 'changed' and not sqla.session.is_modified(model): return msg = '{} {} by {}\n\n'.format(model.__class__.__name__, state, get_user().full_name) view = model_views[model.__class__] attrs = inspect(model).attrs for (col, _) in self.get_list_columns(): attr = attrs[col] prop = model.__class__.__mapper__.attrs[col] is_list = isinstance(prop, RelationshipProperty) and prop.uselist changed = state == 'changed' and attr.history.has_changes() if changed and isinstance(attr.value, datetime.datetime): old = attr.history.deleted[0] val = attr.value if old: # special case: convert attr.value from naive to aware datetime val = val.replace(tzinfo=old.tzinfo) changed = val != old if changed: if is_list: msg += '**{}: {}**\n'.format( attr.key, ', '.join( list(map(str, attr.history.unchanged)) + [ '-{}'.format(val) for val in attr.history.deleted ] + ['+{}'.format(val) for val in attr.history.added])) else: msg += '**{}: {} -> {}**\n'.format(attr.key, attr.history.deleted[0], attr.history.added[0]) else: if is_list: msg += '{}: {}\n'.format(attr.key, ', '.join(map(str, attr.value))) else: msg += '{}: {}\n'.format(attr.key, attr.value) if state != 'deleted': msg += '\n' + url_for( view.endpoint + '.edit_view', id=model.id, _external=True) config.log_admin_audit(self, model, msg)
def print_all(self): folders = list(self._get_folders_with_counts()) for folder, _ in folders: documents = self._query_unprinted(folder).all() config.print_documents( doc_paths=[document_path(doc.id) for doc in documents], cover_text=None, printer=config.FS_CONFIG['OFFICES'][self._get_location()]['printers'][0], user=get_user().username, usercode=config.PRINTER_USERCODES['internal'], job_title='Odie-Druck: Ordnerdruck' ) folder.printed_docs += documents sqla.session.commit() return self.render('print_for_folder.html', printed_folders=folders, examinants_without_folders=Examinant.query.filter(~Examinant.folders.any()).sort_by(Examinant.name).all())
def merge(self, ids): lectures = Lecture.query.filter(Lecture.id.in_(ids)).all() first = next(lecture for lecture in lectures if lecture.id == int(ids[0])) all_docs = set() for lecture in lectures: all_docs |= set(lecture.documents) if lecture != first: sqla.session.delete(lecture) first.documents = list(all_docs) sqla.session.commit() flash('Vorlesungen in "{}" zusammengefügt'.format(first.name)) config.log_admin_audit( self, first, '\n\n'.join([ 'Lectures merged by {}'.format(get_user().full_name), '{}->{}'.format([l.name for l in lectures], first.name), url_for('lecture.edit_view', id=first.id, _external=True) ]))
def print_all(self): folders = list(self._get_folders_with_counts()) for folder, _ in folders: documents = self._query_unprinted(folder).all() config.print_documents( doc_paths=[document_path(doc.id) for doc in documents], cover_text=None, printer=config.FS_CONFIG['OFFICES'][ self._get_location()]['printers'][0], user=get_user().username, usercode=config.PRINTER_USERCODES['internal'], job_title='Odie-Druck: Ordnerdruck') folder.printed_docs += documents sqla.session.commit() return self.render('print_for_folder.html', printed_folders=folders, examinants_without_folders=Examinant.query.filter( ~Examinant.folders.any()).sort_by( Examinant.name).all())
def _log_model_changes(self, model, state): if state == 'changed' and not sqla.session.is_modified(model): return msg = '{} {} by {}\n\n'.format(model.__class__.__name__, state, get_user().full_name) view = model_views[model.__class__] attrs = inspect(model).attrs for (col, _) in self.get_list_columns(): attr = attrs[col] prop = model.__class__.__mapper__.attrs[col] is_list = isinstance(prop, RelationshipProperty) and prop.uselist changed = state == 'changed' and attr.history.has_changes() if changed and isinstance(attr.value, datetime.datetime): old = attr.history.deleted[0] val = attr.value if old: # special case: convert attr.value from naive to aware datetime val = val.replace(tzinfo=old.tzinfo) changed = val != old if changed: if is_list: msg += '**{}: {}**\n'.format(attr.key, ', '.join( list(map(str, attr.history.unchanged)) + ['-{}'.format(val) for val in attr.history.deleted] + ['+{}'.format(val) for val in attr.history.added] )) else: msg += '**{}: {} -> {}**\n'.format( attr.key, attr.history.deleted[0], attr.history.added[0] ) else: if is_list: msg += '{}: {}\n'.format(attr.key, ', '.join(map(str, attr.value))) else: msg += '{}: {}\n'.format(attr.key, attr.value) if state != 'deleted': msg += '\n' + url_for(view.endpoint + '.edit_view', id=model.id, _external=True) config.log_admin_audit(self, model, msg)
def documents_query(): query = Document.query.options(subqueryload('lectures'), subqueryload('examinants')) param_filters = json.loads(request.args.get('filters', '{}')) for id_ in param_filters.get('includes_lectures', []): query = query.filter(Document.lectures.any(Lecture.id == id_)) for id_ in param_filters.get('includes_examinants', []): query = query.filter(Document.examinants.any(Examinant.id == id_)) # From the documentselection view (which can be accessed anonymously) we usually get a GET parameter 'q' like this: # {"operator":"order_by_desc","column":"date","value":{"operator":"and","value":[{"column":"document_type","operator":"in_","value":["written","oral","oral reexam","mock exam"]}]},"page":1} # We need to parse and create the SQLAlchemy Query manually to avoid data leaks. # (If we were instead using jsonquery, anonymous users were able to efficiently extract submitted_by names by # performing bisection with the submitted_by parameter and `like` operator. # See https://github.com/fsmi/odie-server/pull/168 for details.) # For logged in users, we have set allow_insecure_authenticated=True on the /api/documents view to allow # the `submitted_by` filter in the depositreturn view; logged in users have access to all the data anyway. # Note that the `page` parameter is taken into account by endpoint()/filtered_results() in either case. if not get_user(): param_q = json.loads(request.args.get('q', '{}')) # While it looks ugly, we validate all parameters to ensure that the JSON structure is as expected and that only # whitelisted values work. if param_q.get('operator') in ('order_by_desc', 'order_by_asc') and \ param_q.get('column') in ('date', 'number_of_pages', 'validation_time'): sort_fn = asc if param_q.get('operator') == 'order_by_asc' else desc query = query.order_by(sort_fn(param_q['column'])) if isinstance(param_q.get('value'), dict) and \ param_q['value'].get('operator') == 'and' and \ isinstance(param_q['value'].get('value'), list) and \ len(param_q['value']['value']) == 1 and \ param_q['value']['value'][0].get('column') == 'document_type' and \ param_q['value']['value'][0].get('operator') == 'in_' and \ isinstance(param_q['value']['value'][0].get('value'), list) and \ all(value in ('written', 'oral', 'oral reexam', 'mock exam') for value in param_q['value']['value'][0]['value']): query = query.filter(Document.document_type.in_(param_q['value']['value'][0]['value'])) return query
def accept_erroneous_sale(data): db.accounting.log_erroneous_sale(data['amount'], get_user(), data['cash_box']) sqla.session.commit() return {}
def is_accessible(self): return bool(get_user() and get_user().has_permission(*self.allowed_roles))
def _get_location(self): # Nobody would ever be a member of both groups at the same time, right? if get_user().has_permission('info_protokolle'): return 'FSI' if get_user().has_permission('mathe_protokolle'): return 'FSM'
def log_donation(data): db.accounting.log_donation(get_user(), data['amount'], data['cash_box']) sqla.session.commit() return {}
def user_info(): # Explicitly pass the csrf token cookie value for cross-origin clients. return serialize({ 'user': get_user(), 'token': csrf._get_token() }, LoginDumpSchema)
from selenium import webdriver from selenium.webdriver.common.keys import Keys import time from login import get_user SEARCH_URL = "https://www.facebook.com/search/videos/?q={}" LOGIN_URL = "https://www.facebook.com/" DRIVER_DIR = '/Users/temp/Project_FC/chromedriver' ID, PW = get_user() def facebook_scrap(keyword): try: driver = webdriver.Chrome(DRIVER_DIR) driver.implicitly_wait(10) driver.get(LOGIN_URL) time.sleep(1) e = driver.find_element_by_id('email') e.clear() e.send_keys(ID) e = driver.find_element_by_id('pass') e.clear() e.send_keys(PW) e.send_keys(Keys.ENTER) print('로그인') driver.get(SEARCH_URL.format(keyword)) links = [] for link in driver.find_elements_by_css_selector('div._14ax > a'): # 링크 a 태그 찾기 links.append(link.get_attribute('href'))
def logout(): Cookie.query.filter_by(user_id=get_user().id).delete() sqla.session.commit() response = app.make_response(redirect(request.args.get('target_path'))) response.set_cookie(AUTH_COOKIE, value='', expires=0) return response
def log_donation(data): db.accounting.log_donation(get_user(), data['amount'], data['cash_box']) sqla.session.commit() return {}
def accept_erroneous_sale(data): db.accounting.log_erroneous_sale(data['amount'], get_user(), data['cash_box']) sqla.session.commit() return {}
def is_accessible(self): return bool(get_user() and get_user().has_permission(*self.allowed_roles))
def _get_location(self): # Nobody would ever be a member of both groups at the same time, right? if get_user().has_permission('info_protokolle'): return 'FSI' if get_user().has_permission('mathe_protokolle'): return 'FSM'
def print_documents(): # GET params could be too limited. therefore, cookies. try: data = PrintJobLoadSchema().loads(urllib.parse.unquote(request.cookies['print_data'])) except marshmallow.exception.ValidationError as e: raise ClientError(str(e), status=500) document_ids = data['document_ids'] app.logger.info("Printing document ids {} ({} in total) on {} for {}".format(document_ids, len(document_ids), data['printer'], data['cover_text'])) document_objs = Document.query.filter(Document.id.in_(document_ids)).all() if any(not doc.has_file for doc in document_objs): raise ClientError('Tried to print at least one document without file', status=400) # sort the docs into the same order they came in the request docs_by_id = {doc.id: doc for doc in document_objs} documents = [docs_by_id[id] for id in document_ids] assert data['deposit_count'] >= 0 print_price = sum(doc.price for doc in documents) # round up to next 10 cents print_price = 10 * (print_price//10 + (1 if print_price % 10 else 0)) assert print_price + data['deposit_count'] * config.FS_CONFIG['DEPOSIT_PRICE'] == data['price'] # pull current user out of app context user = get_user() yield None # exit application context, start event stream num_pages = sum(doc.number_of_pages for doc in documents) try: if documents: db.accounting.log_exam_sale(num_pages, print_price, user, data['cash_box']) for _ in range(data['deposit_count']): lectures = Lecture.query.filter(Lecture.documents.any(Document.id.in_(document_ids))).all() dep = Deposit( price=config.FS_CONFIG['DEPOSIT_PRICE'], name=data['cover_text'], by_user=user.full_name, lectures=lectures) sqla.session.add(dep) db.accounting.log_deposit(dep, user, data['cash_box']) if documents: try: name = data['cover_text'].split(' ')[0] for _ in config.print_documents( doc_paths=[document_path(doc.id) for doc in documents], cover_text=data['cover_text'], printer=data['printer'], user=user.username, usercode=config.PRINTER_USERCODES[data['cash_box']], job_title="Odie-Druck für {} [{} Seiten]".format(name, num_pages)): pass except Exception as e: sqla.session.rollback() raise NonConfidentialException('printing failed. Exception: ' + str(e)) from e sqla.session.commit() yield('accounting succeeded', '') except NonConfidentialException as e: raise NonConfidentialException(str(e)) from e except Exception as e: # in case of network troubles, we've just printed a set of documents but screwed up accounting. raise NonConfidentialException('accounting failed. Exception: ' + str(e)) from e yield ('complete', '')
def submit_documents(validated): """Student document submission endpoint POST data must be multipart, with the `json` part conforming to DocumentLoadSchema and the `file` part being a pdf file. Uploaded files are stored in subdirectories below config.DOCUMENT_DIRECTORY. This method may raise AssertionErrors when the user sends invalid data. """ # we can't use @deserialize because this endpoint uses multipart form data data = json.loads(request.form['json']) if get_user(): try: data = FullDocumentLoadSchema().load(data) except marshmallow.exceptions.ValidationError as e: raise ClientError(str(e), status=500) if (data.get('document_type') == 'written' and data.get('solution') not in ['official', 'inofficial', 'none'] or data.get('document_type') != 'written' and data.get('solution') is not None): raise ClientError('Invalid value.', status=400) else: try: data = DocumentLoadSchema().load(data) except marshmallow.exceptions.ValidationError as e: raise ClientError(str(e), status=400) assert 'file' in request.files file = request.files['file'] if not _allowed_file(file.filename): raise ClientError('file extension not allowed', status=406) lectures = _match_lectures(data['lectures'], validated) examinants = _match_examinants(data['examinants'], validated) date = data['date'] if not get_user(): assert date <= datetime.date.today() doc_type = data.get('document_type') student_name = data.get('student_name') if student_name is None or student_name.isspace(): student_name = None deposit_return_eligible = student_name is not None early_document_eligible = student_name is not None and doc_type == 'oral' and any( lecture.early_document_eligible for lecture in lectures) new_doc = Document( department=data['department'], lectures=lectures, examinants=examinants, date=date, number_of_pages=0, # will be filled in later or upon validation document_type=doc_type, validation_time=datetime.datetime.now() if validated else None, comment=data.get('comment'), solution=data.get('solution'), submitted_by=student_name, early_document_eligible=early_document_eligible, deposit_return_eligible=deposit_return_eligible) sqla.session.add(new_doc) # we have the db side of things taken care of, now save the file # and tell the db where to find the file sqla.session.flush() # necessary for id field to be populated save_file(new_doc, file) if validated: # we don't trust other people's PDFs... new_doc.number_of_pages = number_of_pages(new_doc) sqla.session.commit() app.logger.info("New document submitted (id: {})".format(new_doc.id)) if validated: config.document_validated(document_path(new_doc.id)) return {'early_document_eligible': early_document_eligible}
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # cache DB operation self._authenticated = bool(get_user())
def user_info(): # Explicitly pass the csrf token cookie value for cross-origin clients. return serialize({'user': get_user(), 'token': csrf._get_token()}, LoginDumpSchema)