Techniques for adding Achievements to business class software - language-agnostic

I was thinking of adding some Achievements to our internal bug-tracking and time logging system. It's connected to an SQL Server back-end.
At first I thought that the system could be run on the database, using triggers to, for example, know when:
you've logged 1000 hours
created 1000 tickets
closed your own ticket
worked on a ticket that has been not touched in a while.
etc (you know - database-ish things)
But then I realized that I would also want purely front-end achivements
used the advanced search abiltiy
sorted by a column
reset settings to default
searched 500 times
It seems like the logic of every achievement must be hand coded. Could anyone imagine an some sort of Achievements Rules Engine, that you for example create scripts for?
And how to store them? If the achievement is:
change column sort order 50 times in one session
that would imply that every time they sort a listview column it updates the database.
Any thoughts on this Win32 application design problem? I don't think that the Gang of Four have an Achievements design pattern.
Note: It's a Win32 client application, not a web-site.
i definetly like the idea of an eventing system. Various actions the user takes can raise events through a single eventing object:
protected void TimeEntriesListView_ColumnSort(object sender, EventArgs e)
{
_globalListener.RaiseEvent(EventType.ListViewColumnSort, sender, e);
}
protected void TimeEntriesListView_ColumnDrag(object sender, EventArgs e)
{
_globalListener.RaiseEvent(EventType.ListViewColumnDrag, sender, e);
}
That object can then have logic added to it to decide which events it wants to count. But more reasonably, various event listeners can attached to the central event listener, and have their custom achievement logic.

The trick isn't the coding of the rules, really, those are straightforward, and can perhaps be simple expressions (number_of_bugs > 1000).
The trick is accumulating the statistics. You should look at a form of Event Stream Processing to record your achievements. For example, you don't really want to implement the "1000 bugs" achievement with "select count(*) from bugs where user= :user" all the time (you could do it this way, but arguably shouldn't). Rather you should get an event every time they post a bug, and you achievement system records "found another bug". Then the rule can check the "number_of_bugs > 1000".
Obviously you may need to "prime the pump" so to speak, setting the number_of_bugs to the current number in the DB when you start the achievement system up.
But the goal is to keep the actual event processing light weight, so let it track the events as they go by with all of the events running on a common, internal pipe or bus.
A scripting language is a good idea as well, as they can easily evaluate both expressions and more complicated logic. "number_of_bugs > 1000" is a perfectly valid Javascript program for example. The game is just getting the environment set up.
You can also store the scripts in the DB and make an "achievement" editor to add them on the fly if you like, assuming you're capturing all of the relevant events.

I am also trying to figure out how to build an achievements rules engine.
I checked around rules engines and what are the basics of it. There is a good summary of that in wikipedia
Basically, you either have foward chaining when you, for example, check availability for something; otherwise, you use Event Condition Action rules. Its the latest that caught my attention.
So you could predefine a set of triggered events. In the engine you can define what are the checked conditions who are based on available metrics, and specify the association achievement.
This way seems more flexible, but you have to define the events, possible condition checks and metrics.
For example, you would have various events like TicketCreated triggered in the code.
In the rule engine you could have something like
Event: TicketCreated
Condition: UserTicketCount >= 1000
Achivement: "Created 1000 tickets"
or for "reset settings to default"
Event: SettingsChanged
Condition: Settings = DEFAULT
Achievement: "Reset to Default"
I haven't tried it yet, but I'm going to pretty soon. It's mostly theoric for now.

The backend achievements should be simple - they seem to be based on already tracked items. The front end, as you correctly stated, will require more tracking. One way you might want to do this is implement an analytics engine for your front end site.
Piwik at http://www.piwik.org might help here - it is a google analytics clone, that you host yourself. The idea would be to use it's logging capabilities to track when an achievement has been completed.
As for some form of scripting of the rules, you will always have to hand code them - you can perhaps make it simpler though, by creating your own small scripting engine, or implementing a custom Lua set.

How about storing the sql query associated with the achievement in the database itself, so adding a new achievement would only involve adding the name and the sql query for it.
for e.g.
you've logged 1000 hours - "select case sum(hours) > 1000 then 'true' else ' false' end from user where user_id = ?"
The UI based achievements would have to have the data stored in the db as well because you may use the same data for other achievements going forward.

I think one table to hold each event that you want to track. Such as "Performed a search". Then each achievement could have an SQL associated with it.
You could then build one trigger, on the Event table, which would compare the achievements that apply to that event, and add the achievement to the Achivement table.
Here are the quickie table designs:
EventItems:
UserId, EventName, DateOccured, AnyOtherInfo
AchievementQualifiers:
Achievement Name, AchievementCheckSQL, EventsThisAppliesTo
(Normalize this, if multiple events apply)
In you System, weather its web or win32, you simply insert into the EventItems table whenever the user does some event that you want to track. You could make one class to handle this. For purely DB type events, such as "Item Posted" or "Comment Posted" you could do this with a trigger instead.
Then, in the triggers on EventItems, for each AchievementQualifer that has that EventThisAppliesTo, run the SQL check, and update an UserAchievement table if it's true and hasn't been awarded yet.
Excuse my spelling, as I'm typing this while sitting in a brew pub.

Related

Is there another way to deal with niche flags in tables?

Say you have a table meal that you have been using in your application for many years. You have always been using this table to serve meals. But now you need to be able to make meal drafts before they are (optionally) made. So you add a new boolean column draft to the meal table; these meals are not to be served since the user has not confirmed that they want to make this particular meal. So they need to only be available in the draft dialog and nowhere else.
The drafts need to be persisted since a user might close the dialog and want to get back to the preview later.
Actual meal serving is still the backbone of your application. So now you need to update all of the queries to check that this flag is false. This is a lot of work and you have to be very careful, since forgetting one query might turn into a pretty subtle bug.
You could perhaps duplicate the meal table creation and make a new table called draft_meal, but this is the central table in your application (related to many other tables) and you have to update meal to add things like new columns etc. pretty regularly.
Is there another way to deal with this? Is there a way to deal with niche flags that “pollute” the whole application?
You can change the name of your existing table to something like meal_master as you add the new column. Then create a view something like this:
CREATE OR REPLACE VIEW meal AS
SELECT col, col, ...
FROM meal_master
WHERE draft = 0
That approach allows you to keep your existing code going as you add your new feature. You can go live with less risk to your existing service.
You should still refactor everything to use the new table; you just don't have to do it in a big bang.
Make sure draft's default value is 0.
You're learning that data lives longer than the code that uses it.

How to implement "SQL Transactions" in "Clean Architecture"?

I am working on an Express-based (Nodejs) API that uses MySQL for data persistence. I have tried to follow the CLEAN ARCHITECTURE proposed by Sir R.C. Martin.
Long story short:-
There are some crop vendors and some users. A user can request an order of some crops with a defined quantity from a vendor. This puts the order in PENDING state. Then the vendor will confirm the orders he/she gets from the user.
Domain/Entity -> CROP, Use-case -> add, remove, edit, find, updateQty
Domain/Entity -> ORDER, Use-case -> request, confirm, cancel
I have to implement a confirm order functionality
I have an already recorded order with ordered item list in my DB (order in the pending state)
Now on confirming order action I need to subtract each item quantity from respective crop present in the DB record, with a check that no value turns negative (i.e. no ordered qty is more than present qty)
If it is done for all the items under a "transaction cover" then I have to commit the transaction
Else revert back to the previous state (i.e rollback)
I know how to run Mysql specific transactions using "Sequelize", but with a lot of coupling and poor source code architecture. (If I do it that way, then DB won't be like plugin anymore)
I am not able to understand how to do this while maintaining the architecture and at what layer to implement this transaction thing, use-case/data-access, or what?
Thanks in advance
I recommend keeping the transaction in the "adapters layer" by using "unit of work" pattern. This way the database remains a plug-in to the business logic.

Dynamically Customizable Group-Based Authorization in Rails

Currently, in my app there are just 3 roles visitors can have:
admin that has all privileges
user that can can perform several actions about him/her-self within the system
guest that can just watch and send bug reports
Everything is primitively implemented, as follows: in a DB each user has a field where his being admin (stands for 2 in the field) or user (1) is indicated, and in the application_controller.rb it is just checked if logged_in? && current_user.DB_FIELD == 2 (or > 0), and in the necessary controller there occurs a before_filter check, etc.
However, such a simple implementation worked great till recently when we decided to extend the functionality of the system, that is, partly, to allow admin to join users into groups, but there are some moments. For better understanging of what I am going to ask, let me describe the situation from the way I see it (maybe you can suggest something much better and logical):
I am an admin. I open /groups, and see a list of groups.
What is a group? A group, on the one hand, is a set of permissions, and on the other hand, is a combination of users that should have the same permissions within my app.
What is a permission? A permission is one action that each user of the group it assigned to can perform.
I want to unite new users in one group, but this group doesn't exist. So I click the button (which stands for /groups/new), and the Create Group window pops up. There, I have a textfield for a group name, a bulk of checkboxes, each stands for a permission, a field for adding users, and a Save button. I write the group name, check all the permissions I want to assign to this group, add users to this group (I am going to implement this through ajax search: starting typing a user's name, he/she appears, click Enter, and one user is added, then repeat these actions if needed - is it an OK approach?), and click Save.
Ok, I got a new group with several users. But stop, I realized I forgot to add one more person! I return to the Edit Group window (/groups/edit), and refill the misfilled fields. Click Save - and again some magic (I mean, update operations over the DB).
And so, what I have at the final stage? I can freely c/r/u/d the groups, managing users and permissions in them, and perform it in a very GUI-driven way (I mean, checkboxes, ajax search field, etc.)
For two weeks I have been googling/stackoverflowing/scrutinizing info about rails role- and group-based authorizations; have found a lot of solutions like cancan, easy_roles, troles, etc. gems, but cannot find in any of them how to implement a group-based approach, which is dynamic (or customizable? or dynamically customizable?). The only thing that really 100% suits my needs is a redmine permission and permission group approach, but it is overcomplicated due to its over9000-functionality, so I couldn't even fully understand how it is implemented, let alone implement it on my own.
And the questions are (assuming that the set of permissions is permanent so can be hardcoded, and the set of groups is absolutely free; also, if the user doesn't belong to any group he/she has default user permissions; moreover, permissions are not just for c/r/u/d operations, but also for the manually created methods):
What is the best way to implement the above mentioned system? Any existing yet not found by me gem or approach?
How to painlessly-for-scalability store the permissions and the permission groups? A bitmask, or separate permission, permission-to-group assignment, and group tables?
How to painlessly put users into groups? A group field in the user's DB row, or a separate user-to-group assignment table?
Preferably, that the permissions assigned to the group the user being added to, instantly, without any user relogins, apply to him.
Thank you in advanced!
Through several nights I finally came to a solution, which is, to my mind, rather easy yet powerful, but obviously not the best (but still an) implementation.
So, we have now +1 tables, which is of groups, where the columns are id, name, and permission. The last column is a usual integer which represents all the permissions in a decimal number.
The permissions are "aliased" in the controller: e.g. 1 stands for can_manage_smth, 2 stands for can_view_smth, etc.
The permission choice panel is in the /groups section, and is a simple set of checkboxes, applying an onchange action to each we ajaxly perform an OR operation with the permission stored in the table (e.g. we select 3 checkboxes standing for the values of 1, 8, and 16, then we get in our table 25, which is, in turn, a result of 1 | 8 | 16).
So answering my questions:
Not the best but still a solution.
It almost does not affect the scalability because adding a new permission (which is a very rare action) will just demand a new alias of the permission and its before_filter checkings in the beginning of the controller. And I used a bitmask but not as a binary but just a usual decimal value with which simple binary logic operands can play.
No separate user-to-group assignment tables, just a single group_id column in a user table (which already existed).
Hope everything implemented will work perfectly. If any issues occur, I will indicate here. Also, if any new implementation ideas come.
Anyway, thanks to everybody!

web inserts at the same time

We have developed an online quiz where users can register a team to take part in the quiz.
There is a check in the asp to see if that teamname has already been submitted, and if it has, an error is generated.
We have noticed a problem if 2 teams are registered at exactly the same time with the same name, that both teams are registered. Although this is highly unlikely, we wondered what approach should be used to overcome this.
We are using mySql as the database if that makes any difference.
Thanks for any guidance.
Don't worry this never happens.
Databases are smart enough and handle concurrency isuuses.
If you run a query on database for registering a team and another team register at the same time, at database level the first query (when it's send to database) succeed and the second fails with an error which you should take care of. If registeration needs actions more than a simple insert on a table then you should use transaction objects at your queries/store-procedures.
You can set the name column to be unique, and the database will throw an error on the second insert. If you want to do it in code, it will be more complicated.
Not sure how two teams can register at exactly the same time - even if the requests are submitted simultaneously (down to the nanosecond), your transaction semantics should still guarantee that one of them is "first" from the database point of view.

Keeping Drop-downs DRY in a web app

I'm writing a CMS for various forms and such, and I find I'm creating a lot of drop-downs. I don't really feel like mucking up my database with tons of random key/string value tables for simple drop-downs with 2-4 options that change very infrequently. What do you do to manage this in a responsible way?
This is language-agnostic, but I'm working in Rails, if anyone has specific advice.
We put everything into a single LookUp table in the database, with a column that mapped to an enum that described which lookup it was for (title, country, etc.).
This enabled us to add the flexibility of an "Other, please specify" option in lookup dropdowns. We made a control that encapsulated this, with a property to turn this behaviour on or off on a case-by-case basis.
If the end user picked "Other, please specify", a textbox would appear for them to enter their own value. This would be added to the lookup table, but flagged as an ad hoc item.
The table contained a flag denoting the status of each lookup value: Active, Inactive, AdHoc. Only Active ones would appear in the dropdown; AdHoc ones were those created via the "Other, please specify" option.
An admin page showed the frequency of usage of the AdHoc values, allowing the administrators of the site to promote common popular values into general usage (i.e. changing their Status flag to Active).
This may well be overkill for your app, but it worked really well for ours: the app was basically almost entirely CRUD operations on very business-specific data. We had dozens of lookups throughout the site that the customer wanted to be able to maintain themselves. This gave them total flexibility with no intervention from us.
You cold have one single dropdown table with an extra column to say what the drop down is for... limit the results with a where clause...
At my current position, we implemented a LookupCode table that contains a CodeGroup,Code, and Meaning column, as well as some others (like active). That way you have a single table that contains all of your lookup values are in a single location and you can do some quick lookups to bind to your dropdown lists.