diff --git a/bos_mint/forms.py b/bos_mint/forms.py index 3400d70..36f01df 100644 --- a/bos_mint/forms.py +++ b/bos_mint/forms.py @@ -266,6 +266,10 @@ def fill(self, selectedObject): self.name.fill(selectedObject['name']) +class DeleteForm(FlaskForm): + cancel = SubmitField("Cancel") + submit = SubmitField("Delete") + class NewEventGroupForm(FlaskForm): sport = SelectField("Sport", validators=[DataRequired()], choices=None) diff --git a/bos_mint/node.py b/bos_mint/node.py index 31be7e3..2af881c 100644 --- a/bos_mint/node.py +++ b/bos_mint/node.py @@ -1,18 +1,18 @@ -from . import wrapper - from functools import wraps + +from bookied_sync.lookup import Lookup from peerplays.account import Account -from peerplays.sport import Sport, Sports -from peerplays.eventgroup import EventGroups, EventGroup -from peerplays.event import Events, Event -from peerplays.bettingmarketgroup import BettingMarketGroup, BettingMarketGroups +from peerplays.asset import Asset from peerplays.bettingmarket import BettingMarkets, BettingMarket -from peerplays.rule import Rules, Rule +from peerplays.bettingmarketgroup import BettingMarketGroup, BettingMarketGroups +from peerplays.event import Events, Event +from peerplays.eventgroup import EventGroups, EventGroup +from peerplays.instance import shared_peerplays_instance from peerplays.proposal import Proposals +from peerplays.rule import Rules, Rule +from peerplays.sport import Sport, Sports -from peerplays.instance import shared_peerplays_instance -from peerplays.asset import Asset -from bookied_sync.lookup import Lookup +from . import wrapper class NodeException(Exception): @@ -368,6 +368,29 @@ def createSport(self, istrings): except Exception as ex: raise NodeException(ex.__class__.__name__ + ": " + str(ex)) + @proposedOperation + def deleteSport(self, sportId): + try: + return self.get_node().sport_delete( + sport_id=sportId, + account=self.getSelectedAccountName(), + append_to=self.getPendingProposal() + ) + except Exception as ex: + raise NodeException(ex.__class__.__name__ + ": " + str(ex)) + + @proposedOperation + def deleteEventgroup(self, event_group_id): + + try: + return self.get_node().eventgroup_delete( + event_group_id=event_group_id, + account=self.getSelectedAccountName(), + append_to=self.getPendingProposal() + ) + except Exception as ex: + raise NodeException(ex.__class__.__name__ + ": " + str(ex)) + @proposedOperation def createEventGroup(self, istrings, sportId): try: @@ -522,6 +545,17 @@ def rejectProposal(self, proposalId): except Exception as ex: raise NodeException(ex.__class__.__name__ + ": " + str(ex)) + @proposedOperation + def deleteProposal(self, proposalId): + try: + return self.get_node().deleteproposal( + proposalId, + "witness-account", + append_to=self.getPendingProposal() + ) + except Exception as ex: + raise NodeException(ex.__class__.__name__ + ": " + str(ex)) + @proposedOperation def resolveBettingMarketGroup(self, bettingMarketGroupId, resultList): try: diff --git a/bos_mint/templates/widgets/operationContainer.html b/bos_mint/templates/widgets/operationContainer.html index 765a638..254efb8 100644 --- a/bos_mint/templates/widgets/operationContainer.html +++ b/bos_mint/templates/widgets/operationContainer.html @@ -11,7 +11,7 @@ {% else %} - \ No newline at end of file + diff --git a/bos_mint/templates/widgets/operation_event_group_delete.html b/bos_mint/templates/widgets/operation_event_group_delete.html new file mode 100644 index 0000000..2aa65a3 --- /dev/null +++ b/bos_mint/templates/widgets/operation_event_group_delete.html @@ -0,0 +1,12 @@ +{% from "_macros.html" import render_int_string %} +
+ +
+
Event group deletion {{ operationId }}
+
+ Event group ID: {{ data['event_group_id']}} + +

+
+
+
\ No newline at end of file diff --git a/bos_mint/templates/widgets/operation_sport_delete.html b/bos_mint/templates/widgets/operation_sport_delete.html new file mode 100644 index 0000000..d81918c --- /dev/null +++ b/bos_mint/templates/widgets/operation_sport_delete.html @@ -0,0 +1,10 @@ +{% from "_macros.html" import render_int_string %} +
+ +
+
Sport deletion {{ operationId }}
+
+ Sport ID: {{ data['sport_id'] }} +
+
+
\ No newline at end of file diff --git a/bos_mint/utils.py b/bos_mint/utils.py index cd989e8..8ba5c49 100644 --- a/bos_mint/utils.py +++ b/bos_mint/utils.py @@ -95,36 +95,24 @@ 'bet': 'bettingmarket' } -TYPENAME_TO_NEWOP_MAP = { - 'sport': 'sport_create', - 'eventgroup': 'event_group_create', - 'event': 'event_create', - 'event_status': None, - 'bettingmarketgroup': 'betting_market_group_create', - 'bettingmarketgrouprule': 'betting_market_rules_create', - 'bettingmarket': 'betting_market_create', - 'bet': 'bet_create', -} - -TYPENAME_TO_UPDATEOP_MAP = { - 'sport': 'sport_update', - 'eventgroup': 'event_group_update', - 'event': 'event_update', - 'event_status': 'event_update_status', - 'bettingmarketgroup': 'betting_market_group_update', - 'bettingmarketgrouprule': 'betting_market_rules_update', - 'bettingmarket': 'betting_market_update' -} - -UPDATEOP_TO_TYPENAME_MAP = { +OP_TO_TYPENAME_MAP = { + 'sport_create': 'sport', + 'sport_delete': 'sport', 'sport_update': 'sport', + 'event_group_create': 'eventgroup', + 'event_group_delete': 'eventgroup', 'event_group_update': 'eventgroup', + 'event_create': 'event', 'event_update': 'event', 'event_update_status': 'event_status', + 'betting_market_group_create': 'bettingmarketgroup', 'betting_market_group_update': 'bettingmarketgroup', + 'betting_market_rules_create': 'bettingmarketgrouprule', 'betting_market_rules_update': 'bettingmarketgrouprule', + 'betting_market_create': 'bettingmarket', 'betting_market_update': 'bettingmarket', - 'betting_market_group_resolve': 'bettingmarketgroup' + 'betting_market_group_resolve': 'bettingmarketgroup', + 'bet_create': 'bet', } @@ -340,12 +328,7 @@ def getProposalOperations(tx): operation = operation['op'] operationName = getOperationNameForId(operation[0]) typeName = 'unknown' - for tmpTypeName, newOpName in TYPENAME_TO_NEWOP_MAP.items(): - if newOpName == operationName: - typeName = tmpTypeName - - typeName = UPDATEOP_TO_TYPENAME_MAP.get(operationName, typeName) - + typeName = OP_TO_TYPENAME_MAP.get(operationName, typeName) if typeName == 'unknown': raise Exception # this is a hack. proper id construction __must__ be diff --git a/bos_mint/views.py b/bos_mint/views.py index e8d86c9..3f5cb62 100644 --- a/bos_mint/views.py +++ b/bos_mint/views.py @@ -1,3 +1,4 @@ + from flask import ( redirect, request, @@ -22,7 +23,8 @@ ReplayForm, ApprovalForm, BettingMarketGroupResolveForm, - SynchronizationForm + SynchronizationForm, + DeleteForm ) from .models import ( LocalProposal, @@ -54,6 +56,8 @@ from threading import Thread from datetime import datetime from peerplays.event import Events +from peerplays.eventgroup import EventGroup +from peerplays.sport import Sport from bos_mint.datestring import string_to_date from pprint import pformat, pprint import json @@ -610,7 +614,19 @@ def overview(typeName=None, identifier=None): def buildListElements(tmpList): tmpList = sorted(tmpList, key=lambda k: k['toString']) for entry in tmpList: - if entry['typeName'] == 'event': + if entry['typeName'] == 'sport': + entry['extraLink'] = [{ + 'title': 'Delete', + 'link': 'sport_delete', + 'icon': 'trash alternate outline' + }] + elif entry['typeName'] == 'eventgroup': + entry['extraLink'] = [{ + 'title': 'Delete', + 'link': 'eventgroup_delete', + 'icon': 'trash alternate outline' + }] + elif entry['typeName'] == 'event': entry['extraLink'] = [{ 'title': 'Show incidents', 'link': 'event_incidents', @@ -836,7 +852,8 @@ def votable_proposals(): if proposals: accountId = Node().getSelectedAccount()['id'] - containerList = widgets.prepareProposalsDataForRendering(proposals) + advanced_user = Config.get("advanced_features", default=False) + containerList = widgets.prepareProposalsDataForRendering(proposals, accountId=accountId, advanced_user=advanced_user) containerReview = {} reviewedProposals = LocalProposal.getAllAsList() @@ -876,6 +893,18 @@ def votable_proposals_reject(proposalId): return redirect(url_for('votable_proposals')) +@app.route("/proposals/delete/", methods=['post', 'get']) +@unlocked_wallet_required +def proposal_delete(proposalId): + try: + Node().deleteProposal(proposalId) + flash('Added Proposal delete proposal to Pending operations') + except Exception as e: + flash(e.__repr__(), category="error") + + return redirect(url_for('votable_proposals')) + + def findAndProcessTranslatons(form): for field in form._fields.values(): if isinstance(field, FormField) and isinstance(field.form, TranslatedFieldForm) and field.addLanguage.data: @@ -926,6 +955,7 @@ def sport_new(): return genericNewForm(forms.NewSportForm) + @app.route("/eventgroup/new", methods=['post', 'get']) @app.route("/eventgroup/new/", methods=['post', 'get']) @unlocked_wallet_required @@ -964,7 +994,51 @@ def bet_new(): return render_template_menuinfo('index.html', **locals()) +@app.route("/sport/delete/", methods=['post', 'get']) +@app.route("/sport/delete/", methods=['post', 'get']) +@unlocked_wallet_required +def sport_delete(selectId=None): + """If this button is pressed a form will be opened to delete the Sport with a given selectId""" + form = DeleteForm(sport_id=selectId) + sport = Sport(selectId) + formTitle = "Are you sure that you want do delete {0} ({1})?".format(sport['name'][0][1], sport['id']) + flash('All related Eventgroups will be cancelled and deleted as well', category='warning') + + if form.validate_on_submit(): + if form.cancel.data: # cancel button has been pressed + return redirect(url_for('overview')) + + try: + Node().deleteSport(selectId) + return redirect(url_for('overview')) + except Exception as e: + flash(e.__repr__(), category='error') + + return render_template_menuinfo("generic.html", **locals()) + + +@app.route("/eventgroup/delete/", methods=['post', 'get']) +@app.route("/eventgroup/delete/", methods=['post', 'get']) +@unlocked_wallet_required +def eventgroup_delete(selectId=None): + form = DeleteForm() + eventgroup = EventGroup(selectId) + print(eventgroup) + formTitle = "Are you sure that you want to delete {0} ({1})?".format(eventgroup['name'][0][1], eventgroup['id']) + flash("All related Events will be cancelled and deleted as well", category='warning') + if form.validate_on_submit(): + if form.cancel.data: + return redirect(url_for('overview')) + try: + Node().deleteEventgroup(event_group_id=selectId) + return redirect(url_for('overview')) + except Exception as e: + flash(e.__repr__(), category="error") + + return render_template_menuinfo("generic.html", **locals()) + def genericUpdate(formClass, selectId, removeSubmits=False, details=False): + typeName = formClass.getTypeName() selectFunction = utils.getTypeGetter(typeName) diff --git a/bos_mint/widgets.py b/bos_mint/widgets.py index c148802..7f0231d 100644 --- a/bos_mint/widgets.py +++ b/bos_mint/widgets.py @@ -110,7 +110,7 @@ def addOperation(self, operationId, data): self.template_args['operations'].append(ow) -def prepareProposalsDataForRendering(proposals, accountId=None): +def prepareProposalsDataForRendering(proposals, accountId=None, advanced_user=False): tmpList = [] for proposal in proposals: # ensure the parent expiration time is the shortest time @@ -151,7 +151,10 @@ def prepareProposalsDataForRendering(proposals, accountId=None): buttonNegativeURL=url_for('votable_proposals_reject', proposalId=proposal['id']), buttonPositiveURL=url_for('votable_proposals_accept', - proposalId=proposal['id']) + proposalId=proposal['id']), + buttonDelete="Delete", + buttonDeleteURL=url_for("proposal_delete", proposalId=proposal['id']), + advanced_user=advanced_user ) for operation in proposal['proposed_transaction']['operations']: diff --git a/bos_mint/wrapper.py b/bos_mint/wrapper.py index 3ab494e..0a67c41 100644 --- a/bos_mint/wrapper.py +++ b/bos_mint/wrapper.py @@ -30,7 +30,11 @@ def kwGet(self, kwargs, name, default=None): if default: return kwargs.get(name, default) else: - return kwargs[name] + try: + tmpname = kwargs[name] + except KeyError: + tmpname = "to be deleted" + return tmpname class Sport(BlockchainIdentifiable): @@ -44,6 +48,11 @@ def translateOperation(cls, operationData): return {'pendingOperationId': operationData['operationId'], 'id': operationData['sport_id'], 'name': operationData['new_name']} + elif operationData.get('operationName', None) == 'sport_delete': + return {'pendingOperationId': operationData['operationId'], + 'id': operationData['sport_id'], + 'name': 'To be deleted', + } else: from .node import NodeException raise NodeException( @@ -69,6 +78,12 @@ def translateOperation(cls, operationData): 'id': operationData['event_group_id'], 'name': operationData['new_name'], 'sport_id': operationData['new_sport_id']} + elif operationData.get('operationName', None) == 'event_group_delete': + return { + 'pendingOperationId': operationData['operationId'], + 'id': operationData['operationId'], + 'name': 'To be deleted', + } else: from .node import NodeException raise NodeException( diff --git a/requirements.txt b/requirements.txt index 7c5aceb..cdf9f2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ Jinja2 Markdown PyYAML requests -webassets>=0-12.1 +webassets>=0.12.1 flask-script flask-sqlalchemy mysqlclient