PK!W)Nchatql/__init__.py__version__ = '0.1.0' from chatql.chatql import * from chatql import engine from chatql import mongodb_client from chatql import matcherPK! chatql/chatql.py# coding=utf-8 # # Licensed under the MIT License """GraphQL scheme define.""" import graphene import json class User(graphene.ObjectType): """User Type.""" id = graphene.ID() optional_args = graphene.String() class Response(graphene.ObjectType): """System Response Type.""" id = graphene.ID() user = graphene.Field(User) request = graphene.String(description='User input request') text = graphene.String(description='System response string') class Query(graphene.ObjectType): """Query Type.""" response = graphene.Field( Response, request=graphene.String(required=True), user=graphene.ID()) user = graphene.Field( User, id=graphene.ID(), optional_args=graphene.String()) def resolve_response(self, info, request=None, user=None): """Request param resolver.""" engine = info.context['engine'] res_history = engine.generate_response_text( request, user=user) return Response( id=res_history.id, request=request, user=User(id=res_history.user.id), text=res_history.scenario['response']) def resolve_user(self, info, id=None, optional_args=None): """User param resolver.""" if id is None: engine = info.context['engine'] id = engine.get_user_id(**json.loads(optional_args)) if id is None: return User() if optional_args is None: engine = info.context['engine'] attributes = engine.get_user_attributes(id) optional_args = json.dumps(attributes) return User(id=id, optional_args=optional_args) class CreateUser(graphene.Mutation): """Crete User Mutation.""" class Arguments: """Mutation Arguments.""" optional_args = graphene.String() user = graphene.Field(lambda: User) def mutate(self, info, optional_args=None): """Mutate function.""" engine = info.context['engine'] if optional_args is None: id = engine.create_new_user() else: id = engine.create_new_user(**json.loads(optional_args)) user = User(id=id, optional_args=optional_args) return CreateUser(user=user) class Mutations(graphene.ObjectType): """Chatql Schema Mutations.""" create_user = CreateUser.Field() schema = graphene.Schema(query=Query, mutation=Mutations) PK!TC>chatql/engine.py# coding=utf-8 # # Licensed under the MIT License """Dialog Management Module.""" from chatql.matcher import RegexMatcher class DialogEngine(object): """Dialog Management Module.""" def __init__(self, client): """Dialogengine Constructor. Args: client (object): Database access client instance. """ self._client = client def generate_response_text(self, request, user=None, **context): """Generate Response Text using DB and Intent Estimator. Args: request (str): user input text user (str): (Optional) user specified id.When user is None, system create new user context (dict): other values for generating response Return: response (str): response text. when no response text is None. """ if user is None: user = self.create_new_user() matcher = { "regex": RegexMatcher(request) } global_values = dict( **matcher, **context, **self._client.globals(user)) for s in self._client.scenarios: conditions = s.conditions.strip() if eval(conditions, global_values, {"attributes": s.attributes}): return self._client.save_history(request, s, user) return self._client.save_history(request, None, user) def create_new_user(self, **option): """Create new user. Args: option (sict): Optional attributes dictionary Return: ID (str): new user id """ return self._client.create_new_user(**option).id def get_user_attributes(self, user_id): """Get user. Args: user_id (str): target user id Return: user (dict): User attributes dictionary. return None, case taget user doesn't exist. """ return self._client.get_user_attributes(user_id) def get_user_id(self, **attributes): """Get user id. Args: atrributes (dict): target user attributes Return: ID (string): User id. return None, case taget user doesn't exist. """ return self._client.get_user_id(**attributes) PK!¾55chatql/matcher/__init__.pyfrom chatql.matcher.regex_matcher import RegexMatcherPK!#+Vchatql/matcher/regex_matcher.py# coding=utf-8 # # Licensed under the MIT License """Regex request matcher.""" import re class RegexMatcher(object): """Regex request matcher.""" def __init__(self, request): """Regex Matcher Constructor. Args: request (str): user input request for match """ self._request = request def __call__(self, pattern): """Match pattern and request. Args: pattern (str): regex pattern string. Return: result (bool): match result. Note: this method calls re.fullmatch """ result = re.fullmatch(pattern, self._request, flags=re.I | re.S) return (result is not None) PK!ochatql/mongodb_client.py# coding=utf-8 # # Licensed under the MIT License """MongoDB client for ChatQL.""" import mongoengine import datetime import json import functools class Scenario(mongoengine.Document): """Scenario Collection Class.""" attributes = mongoengine.DictField() conditions = mongoengine.StringField() response = mongoengine.StringField() created_at = mongoengine.DateTimeField(default=datetime.datetime.utcnow) modified_at = mongoengine.DateTimeField(default=datetime.datetime.utcnow) def save(self, *args, **kwargs): """Override save method for updating modefied datetime.""" self.modified_at = datetime.datetime.utcnow() return super().save(*args, **kwargs) def to_dict(self): """Return dictionary changed object.""" return { "attributes": self.attributes, "conditions": self.conditions, "response": self.response } class User(mongoengine.DynamicDocument): """User Class.""" def to_dict(self): """Return dictionary changed object.""" ret = {} for key in dir(self): if key.startswith("_"): continue if key in ['id', 'objects', 'pk', 'STRICT']: continue obj = getattr(self, key) if callable(obj): continue ret[key] = obj return ret class History(mongoengine.Document): """System Response History Class.""" request = mongoengine.StringField() scenario = mongoengine.DictField() user = mongoengine.ReferenceField(User) created_at = mongoengine.DateTimeField(default=datetime.datetime.utcnow) class MongoClient(object): """MongoDb Client Object.""" def __init__(self, **config): """Mongoclient Constructor.""" mongoengine.connect(**config) @property def scenarios(self): """Return All Scenario Objects.""" return Scenario.objects().order_by('-attributes__priority') def globals(self, user): """Return user usage objects and method.""" objects = { "history": History.objects(user=user), "user": User.objects(id=user) } methods = { "isonce": functools.partial(self.isonce, *[user]), "last_history": functools.partial(self.last_history, *[user]), } return dict( **objects, **methods ) def isonce(self, user, id, period=None): """Check scenrio is once in specific period. Args: user (str): user id used by history filtering id (str): scenario id Return: result (bool): return True case scenario is not in histor, otherwise return False. """ if period is None: period_from = datetime.datetime.min else: period_from = datetime.datetime.utcnow() - period return (History.objects(user=user).filter( scenario__attributes__id=id, created_at__gte=period_from).count() == 0) def last_history(self, user): """Get last history object. Args: user (str): user id used by history filtering Return: result (History): return History object when user's history exists, otherwise return None. """ return History.objects(user=user).order_by('-created_at').first() def create_new_user(self, **option): """Create new user. Return: ID (str): new user id """ u = User(**option) u.save() return u def get_user_attributes(self, user_id): """Get user attributes. Args: user_id (str): target user id Return: user (dict): User attributes dictionary. return None, case taget user doesn't exist. """ user = User.objects(id=user_id).first() if user is None: return None return user.to_dict() def get_user_id(self, **attributes): """Get user id. Args: atrributes (dict): target user attributes Return: ID (string): User id. return None, case taget user doesn't exist. """ user = User.objects(**attributes).first() if user is None: return None return user.id def save_history(self, request, scenario, user): """Save System Response History. Args: request (str): user input request scenario (Scenario or str): response scenario user (id): user id. id must need to be user object id in db """ if isinstance(scenario, Scenario): s = scenario.to_dict() else: s = scenario h = History(request=request, scenario=s, user=user) h.save() return h def import_scenario(self, path): """Import scenario data in DB. Args: path (str): scenario json data path Note: data json format is following. [ { "attributes": { "attributes1": "attributes1 value", ... }, "conditions": "condition string", "response": "response string" }, .... ] """ with open(path, 'r') as f: data = json.load(f) Scenario.objects().insert([Scenario(**d) for d in data]) PK!/33chatql-0.1.1.dist-info/LICENSEMIT License Copyright (c) 2018 Katsuya Shimabukuro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.PK!HnHTUchatql-0.1.1.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!Hchatql-0.1.1.dist-info/METADATATNJ}߯/ Ŏ)t!R6뉳zw]Rڼ$9sf̱0|A9MؘWX6TWwMSabyIe~)òT.QH Ԟ_Krx V\u6}N2bvtICU4az;'ع˅Dj2qt.auYؐS-uŻy҇D~ӓ4MHKR!DW@JjL&$X?k뜕ѥA]ʎ{]DFQ([`\kNJ΍C2m, S蓑VyԌsqϛ̹Ǣ t Ѣ.P ME6 y'XQ錮qXI{@ {%(6MQwR*0nb^)ةު2웩A(Cr[b;ˑE=u:[t=hb5@a$݂ \-  ~;C;D!)NXQso =0oT{]7PQH>5sk}G.~f+Xy-Ui! :L,,Dn!$5^g*)ZZIɰ>t4ɯ0*.:[=W\<EDyA,IOpSkvY=7a_^<utN2=A+dTÄ2n!iƣ3n~'PK!Hchatql-0.1.1.dist-info/RECORDu˒@} tR<@(@eCBDf\97I.(zܩ>~I`aԘchcN@iePBx#?zrjՅڴ. aS?Ly,R=) uVG::dlBlq+*nCcqdn@_GE;ԓK-y7ز |uWvNCl4Koџ:au[Ia.\0EsbX,{ʐY/Y<\JaTʼu# tWػ~Td&44~OR]ԧ*\8nHq9iĞP2b32Q;ݚˈW嫿ncZizqw]s\AG}[NiJPK҄)2CC 1;'X>i^k/"䭆P,^;nwEPK!W)Nchatql/__init__.pyPK! chatql/chatql.pyPK!TC> chatql/engine.pyPK!¾55chatql/matcher/__init__.pyPK!#+VFchatql/matcher/regex_matcher.pyPK!oIchatql/mongodb_client.pyPK!/33K-chatql-0.1.1.dist-info/LICENSEPK!HnHTU1chatql-0.1.1.dist-info/WHEELPK!HH2chatql-0.1.1.dist-info/METADATAPK!H5chatql-0.1.1.dist-info/RECORDPK 7