missionpanel 1.0.1__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {missionpanel-1.0.1 → missionpanel-1.0.2}/PKG-INFO +1 -1
- missionpanel-1.0.2/missionpanel/handler/__init__.py +2 -0
- missionpanel-1.0.2/missionpanel/handler/handler.py +109 -0
- missionpanel-1.0.2/missionpanel/orm/__init__.py +2 -0
- missionpanel-1.0.2/missionpanel/orm/core.py +69 -0
- missionpanel-1.0.2/missionpanel/orm/handler.py +31 -0
- missionpanel-1.0.2/missionpanel/submitter/__init__.py +2 -0
- missionpanel-1.0.2/missionpanel/submitter/abc.py +45 -0
- missionpanel-1.0.2/missionpanel/submitter/asynchronous.py +60 -0
- missionpanel-1.0.2/missionpanel/submitter/submitter.py +56 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/missionpanel.egg-info/PKG-INFO +1 -1
- missionpanel-1.0.2/missionpanel.egg-info/SOURCES.txt +18 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/setup.py +4 -4
- missionpanel-1.0.1/missionpanel.egg-info/SOURCES.txt +0 -9
- {missionpanel-1.0.1 → missionpanel-1.0.2}/LICENSE +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/README.md +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/missionpanel/__init__.py +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/missionpanel.egg-info/dependency_links.txt +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/missionpanel.egg-info/requires.txt +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/missionpanel.egg-info/top_level.txt +0 -0
- {missionpanel-1.0.1 → missionpanel-1.0.2}/setup.cfg +0 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
import abc
|
2
|
+
import datetime
|
3
|
+
from typing import List, Optional, Tuple, Union
|
4
|
+
from sqlalchemy.orm import Session, Query
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
6
|
+
from missionpanel.orm import Mission, Tag, MissionTag, Attempt
|
7
|
+
from sqlalchemy import select, func, distinct, case, Select
|
8
|
+
|
9
|
+
|
10
|
+
class HandlerInterface(abc.ABC):
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def query_missions_by_tag(tags: List[str]) -> Select[Tuple[Mission]]:
|
14
|
+
return (
|
15
|
+
select(Mission)
|
16
|
+
.join(MissionTag)
|
17
|
+
.join(Tag)
|
18
|
+
.filter(Tag.name.in_(tags))
|
19
|
+
.group_by(Mission.id)
|
20
|
+
.having(func.count(distinct(Tag.name)) == len(tags))
|
21
|
+
)
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def query_todo_missions(tags: List[str]) -> Select[Tuple[Mission]]:
|
25
|
+
return (
|
26
|
+
HandlerInterface.query_missions_by_tag(tags)
|
27
|
+
.outerjoin(Attempt)
|
28
|
+
.group_by(Mission.id)
|
29
|
+
.having(func.count(
|
30
|
+
case((( # see if Attempt is finished or working on the Mission
|
31
|
+
Attempt.success.is_(True) | # have finished handler
|
32
|
+
(Attempt.last_update_time + Attempt.max_time_interval >= datetime.datetime.now()) # have working handler
|
33
|
+
), 0), else_=None)) <= 0) # get those Missions that have no finished or working Attempt
|
34
|
+
)
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def create_attempt(session: Union[Session | AsyncSession], mission: Mission, name: str, max_time_interval: datetime.timedelta = datetime.timedelta(seconds=1)) -> Attempt:
|
38
|
+
attempt = Attempt(
|
39
|
+
handler=name,
|
40
|
+
max_time_interval=max_time_interval,
|
41
|
+
content=mission.content,
|
42
|
+
mission=mission)
|
43
|
+
session.add(attempt)
|
44
|
+
return attempt
|
45
|
+
|
46
|
+
|
47
|
+
class Handler(HandlerInterface, abc.ABC):
|
48
|
+
def __init__(self, session: Session, name: str, max_time_interval: datetime.timedelta = datetime.timedelta(seconds=1)):
|
49
|
+
self.session = session
|
50
|
+
self.name = name
|
51
|
+
self.max_time_interval = max_time_interval
|
52
|
+
|
53
|
+
@abc.abstractmethod
|
54
|
+
def select_mission(self, missions: Query[Mission]) -> Optional[Mission]:
|
55
|
+
return missions[0] if missions else None
|
56
|
+
|
57
|
+
def report_attempt(self, mission: Mission, attempt: Attempt):
|
58
|
+
attempt.success = False
|
59
|
+
attempt.last_update_time = datetime.datetime.now()
|
60
|
+
self.session.commit()
|
61
|
+
|
62
|
+
@abc.abstractmethod
|
63
|
+
def execute_mission(self, mission: Mission, attempt: Attempt) -> bool:
|
64
|
+
pass
|
65
|
+
|
66
|
+
def run_once(self, tags: List[str]):
|
67
|
+
missions = self.session.execute(HandlerInterface.query_todo_missions(tags)).scalars().all()
|
68
|
+
mission = self.select_mission(missions)
|
69
|
+
if mission is None:
|
70
|
+
return
|
71
|
+
attempt = HandlerInterface.create_attempt(self.session, mission, self.name, self.max_time_interval)
|
72
|
+
self.report_attempt(mission, attempt)
|
73
|
+
if self.execute_mission(mission, attempt):
|
74
|
+
attempt.success = True
|
75
|
+
self.report_attempt(mission, attempt)
|
76
|
+
return attempt
|
77
|
+
|
78
|
+
|
79
|
+
class AsyncHandler(HandlerInterface, abc.ABC):
|
80
|
+
def __init__(self, session: AsyncSession, name: str, max_time_interval: datetime.timedelta = datetime.timedelta(seconds=1)):
|
81
|
+
self.session = session
|
82
|
+
self.name = name
|
83
|
+
self.max_time_interval = max_time_interval
|
84
|
+
|
85
|
+
@abc.abstractmethod
|
86
|
+
async def select_mission(self, missions: Query[Mission]) -> Optional[Mission]:
|
87
|
+
return missions[0] if missions else None
|
88
|
+
|
89
|
+
async def report_attempt(self, mission: Mission, attempt: Attempt):
|
90
|
+
attempt.last_update_time = datetime.datetime.now()
|
91
|
+
await self.session.commit()
|
92
|
+
await self.session.refresh(attempt)
|
93
|
+
await self.session.refresh(mission)
|
94
|
+
|
95
|
+
@abc.abstractmethod
|
96
|
+
async def execute_mission(self, mission: Mission, attempt: Attempt) -> bool:
|
97
|
+
pass
|
98
|
+
|
99
|
+
async def run_once(self, tags: List[str]):
|
100
|
+
missions = (await self.session.execute(HandlerInterface.query_todo_missions(tags))).scalars().all()
|
101
|
+
mission = await self.select_mission(missions)
|
102
|
+
if mission is None:
|
103
|
+
return
|
104
|
+
attempt = HandlerInterface.create_attempt(self.session, mission, self.name, self.max_time_interval)
|
105
|
+
await self.report_attempt(mission, attempt)
|
106
|
+
if await self.execute_mission(mission, attempt):
|
107
|
+
attempt.success = True
|
108
|
+
await self.report_attempt(mission, attempt)
|
109
|
+
return attempt
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import datetime
|
2
|
+
from typing import List
|
3
|
+
from sqlalchemy import (
|
4
|
+
Column,
|
5
|
+
Integer,
|
6
|
+
Text,
|
7
|
+
JSON,
|
8
|
+
DateTime,
|
9
|
+
ForeignKey
|
10
|
+
)
|
11
|
+
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped
|
12
|
+
from sqlalchemy.ext.asyncio import AsyncAttrs
|
13
|
+
|
14
|
+
|
15
|
+
class Base(AsyncAttrs, DeclarativeBase):
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
class Mission(Base):
|
20
|
+
__tablename__ = "mission"
|
21
|
+
id = Column(Integer, primary_key=True, autoincrement=True, comment="Mission ID")
|
22
|
+
content = Column(JSON, default={}, comment="Mission Content")
|
23
|
+
create_time = Column(DateTime, default=datetime.datetime.now, comment="Mission Create Time")
|
24
|
+
last_update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="Mission Update Time")
|
25
|
+
|
26
|
+
# back populate relationships
|
27
|
+
matchers: Mapped[List['Matcher']] = relationship(back_populates="mission")
|
28
|
+
tags: Mapped[List['MissionTag']] = relationship(back_populates="mission")
|
29
|
+
|
30
|
+
def __repr__(self):
|
31
|
+
return f"Mission(id={self.id}, content={self.content.__repr__()}, create_time={self.create_time.__repr__()}, last_update_time={self.last_update_time.__repr__()})"
|
32
|
+
|
33
|
+
|
34
|
+
class Matcher(Base):
|
35
|
+
__tablename__ = "matcher"
|
36
|
+
pattern = Column(Text, primary_key=True, comment="Matcher Content")
|
37
|
+
|
38
|
+
# relationship
|
39
|
+
mission_id = Column(Integer, ForeignKey("mission.id"), index=True, comment="Mission ID")
|
40
|
+
mission: Mapped['Mission'] = relationship(Mission, back_populates="matchers")
|
41
|
+
|
42
|
+
def __repr__(self):
|
43
|
+
return f"Matcher(pattern={self.pattern.__repr__()}, mission_id={self.mission_id})"
|
44
|
+
|
45
|
+
|
46
|
+
class Tag(Base):
|
47
|
+
__tablename__ = "tag"
|
48
|
+
name = Column(Text, primary_key=True, comment="Tag Name")
|
49
|
+
|
50
|
+
# back populate relationships
|
51
|
+
missions: Mapped[List['MissionTag']] = relationship(back_populates="tag")
|
52
|
+
|
53
|
+
def __repr__(self):
|
54
|
+
return f"Tag(name={self.name.__repr__()})"
|
55
|
+
|
56
|
+
|
57
|
+
class MissionTag(Base):
|
58
|
+
__tablename__ = "missiontag"
|
59
|
+
|
60
|
+
# relationship
|
61
|
+
tag_name = Column(Text, ForeignKey("tag.name"), primary_key=True, comment="Tag")
|
62
|
+
tag: Mapped['Tag'] = relationship(Tag, back_populates="missions")
|
63
|
+
|
64
|
+
# relationship
|
65
|
+
mission_id = Column(Integer, ForeignKey("mission.id"), primary_key=True, comment="Mission ID")
|
66
|
+
mission: Mapped['Mission'] = relationship(Mission, back_populates="tags")
|
67
|
+
|
68
|
+
def __repr__(self):
|
69
|
+
return f"MissionTag(tag_name={self.tag_name.__repr__()}, mission_id={self.mission_id})"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import datetime
|
2
|
+
from sqlalchemy import (
|
3
|
+
Column,
|
4
|
+
Integer,
|
5
|
+
Text,
|
6
|
+
JSON,
|
7
|
+
Boolean,
|
8
|
+
DateTime,
|
9
|
+
Interval,
|
10
|
+
ForeignKey
|
11
|
+
)
|
12
|
+
from sqlalchemy.orm import relationship, Mapped
|
13
|
+
from .core import Base, Mission
|
14
|
+
|
15
|
+
|
16
|
+
class Attempt(Base):
|
17
|
+
__tablename__ = "attempt"
|
18
|
+
id = Column(Integer, primary_key=True, autoincrement=True, comment="Mission ID")
|
19
|
+
handler = Column(Text, comment="Handler Name")
|
20
|
+
create_time = Column(DateTime, default=datetime.datetime.now, comment="Attempt Start Time")
|
21
|
+
last_update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="Attempt Last Update Time")
|
22
|
+
max_time_interval = Column(Interval, default=datetime.timedelta(seconds=1), comment="Attempt Update Time Interval")
|
23
|
+
content = Column(JSON, default={}, comment="Mission Content at that time")
|
24
|
+
success = Column(Boolean, default=False, comment="If this Attempt has succeed")
|
25
|
+
|
26
|
+
# relationship
|
27
|
+
mission_id = Column(Integer, ForeignKey("mission.id"), comment="Mission ID")
|
28
|
+
mission: Mapped['Mission'] = relationship(Mission, backref="attempts")
|
29
|
+
|
30
|
+
def __repr__(self):
|
31
|
+
return f"Attempt(id={self.id}, handler={self.handler.__repr__()}, create_time={self.create_time.__repr__()}, last_update_time={self.last_update_time.__repr__()}, max_time_interval={self.max_time_interval.__repr__()}, content={self.content.__repr__()}, success={self.success}, mission_id={self.mission_id})"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from typing import List, Union, Tuple
|
2
|
+
from sqlalchemy.orm import Session
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
4
|
+
from missionpanel.orm import Mission, Tag, Matcher, MissionTag
|
5
|
+
from sqlalchemy import select, Select
|
6
|
+
|
7
|
+
|
8
|
+
class SubmitterInterface:
|
9
|
+
'''
|
10
|
+
SubmitterInterface is an interface for Submitter and AsyncSubmitter to implement the common methods.
|
11
|
+
There is no anything about session.execute in this class.
|
12
|
+
'''
|
13
|
+
@staticmethod
|
14
|
+
def query_matcher(match_patterns: List[str]) -> Select[Tuple[Matcher]]:
|
15
|
+
return select(Matcher).where(Matcher.pattern.in_(match_patterns)).limit(1).with_for_update()
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def add_mission_matchers(session: Union[Session | AsyncSession], mission: Mission, match_patterns: List[str], existing_matchers: List[Matcher] = []) -> Mission:
|
19
|
+
exist_patterns = [matcher.pattern for matcher in existing_matchers]
|
20
|
+
session.add_all([Matcher(pattern=pattern, mission=mission) for pattern in match_patterns if pattern not in exist_patterns])
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def create_mission(session: Union[Session | AsyncSession], content: str, match_patterns: List[str], existing_mission: Union[Mission | None] = None) -> Mission:
|
24
|
+
if existing_mission is None:
|
25
|
+
mission = Mission(
|
26
|
+
content=content,
|
27
|
+
matchers=[Matcher(pattern=pattern) for pattern in match_patterns],
|
28
|
+
)
|
29
|
+
session.add(mission)
|
30
|
+
else:
|
31
|
+
mission = existing_mission
|
32
|
+
if mission.content != content:
|
33
|
+
mission.content = content
|
34
|
+
return mission
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def query_tag(tags_name: List[str]) -> Select[Tuple[Matcher]]:
|
38
|
+
return select(Tag).where(Tag.name.in_(tags_name)).with_for_update()
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def add_mission_tags(session: Union[Session | AsyncSession], mission: Mission, tags_name: List[str], exist_tags: List[Tag] = [], exist_mission_tags: List[MissionTag] = []):
|
42
|
+
exist_tags_name = [tag.name for tag in exist_tags]
|
43
|
+
session.add_all([Tag(name=tag_name) for tag_name in tags_name if tag_name not in exist_tags_name])
|
44
|
+
exist_mission_tags_name = [tag.tag_name for tag in exist_mission_tags]
|
45
|
+
session.add_all([MissionTag(mission=mission, tag_name=tag_name) for tag_name in tags_name if tag_name not in exist_mission_tags_name])
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from typing import List, Union
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
3
|
+
from missionpanel.orm import Mission
|
4
|
+
from .abc import SubmitterInterface
|
5
|
+
|
6
|
+
|
7
|
+
class AsyncSubmitterInterface(SubmitterInterface):
|
8
|
+
|
9
|
+
@staticmethod
|
10
|
+
async def _query_mission(session: AsyncSession, match_patterns: List[str]) -> Mission:
|
11
|
+
matcher = (await session.execute(SubmitterInterface.query_matcher(match_patterns))).scalars().first()
|
12
|
+
if matcher is None:
|
13
|
+
return None
|
14
|
+
mission = await matcher.awaitable_attrs.mission
|
15
|
+
existing_matchers = await mission.awaitable_attrs.matchers
|
16
|
+
SubmitterInterface.add_mission_matchers(session, mission, match_patterns, existing_matchers)
|
17
|
+
return mission
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
async def _add_tags(session: AsyncSession, mission: Union[Mission | None] = None, tags: List[str] = []):
|
21
|
+
exist_tags = (await session.execute(SubmitterInterface.query_tag(tags))).scalars().all() if len(tags) > 0 else []
|
22
|
+
exist_mission_tags = await mission.awaitable_attrs.tags
|
23
|
+
SubmitterInterface.add_mission_tags(session, mission, tags, exist_tags, exist_mission_tags)
|
24
|
+
|
25
|
+
@staticmethod
|
26
|
+
async def match_mission(session: AsyncSession, match_patterns: List[str]) -> Mission:
|
27
|
+
mission = await AsyncSubmitterInterface._query_mission(session, match_patterns)
|
28
|
+
await session.commit()
|
29
|
+
return mission
|
30
|
+
|
31
|
+
@staticmethod
|
32
|
+
async def create_mission(session: AsyncSession, content: str, match_patterns: List[str], tags: List[str] = []):
|
33
|
+
mission = await AsyncSubmitterInterface._query_mission(session, match_patterns)
|
34
|
+
mission = SubmitterInterface.create_mission(session, content, match_patterns, mission)
|
35
|
+
await AsyncSubmitterInterface._add_tags(session, mission, tags)
|
36
|
+
await session.commit()
|
37
|
+
await session.refresh(mission)
|
38
|
+
return mission
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
async def add_tags(session: AsyncSession, match_patterns: List[str], tags: List[str]):
|
42
|
+
mission = await AsyncSubmitterInterface._query_mission(session, match_patterns)
|
43
|
+
if mission is None:
|
44
|
+
raise ValueError("Mission not found")
|
45
|
+
await AsyncSubmitterInterface._add_tags(session, mission, tags)
|
46
|
+
await session.commit()
|
47
|
+
|
48
|
+
|
49
|
+
class AsyncSubmitter(AsyncSubmitterInterface):
|
50
|
+
def __init__(self, session: AsyncSession):
|
51
|
+
self.session = session
|
52
|
+
|
53
|
+
async def match_mission(self, match_patterns: List[str]) -> Mission:
|
54
|
+
return await AsyncSubmitterInterface.match_mission(self.session, match_patterns)
|
55
|
+
|
56
|
+
async def create_mission(self, content: str, match_patterns: List[str]):
|
57
|
+
return await AsyncSubmitterInterface.create_mission(self.session, content, match_patterns)
|
58
|
+
|
59
|
+
async def add_tags(self, matchers: List[str], tags: List[str]):
|
60
|
+
return await AsyncSubmitterInterface.add_tags(self.session, matchers, tags)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
from typing import List, Union
|
2
|
+
from sqlalchemy.orm import Session
|
3
|
+
from missionpanel.orm import Mission
|
4
|
+
from .abc import SubmitterInterface
|
5
|
+
|
6
|
+
|
7
|
+
class SyncSubmitterInterface(SubmitterInterface):
|
8
|
+
|
9
|
+
@staticmethod
|
10
|
+
def _query_mission(session: Session, match_patterns: List[str]) -> Mission:
|
11
|
+
matcher = session.execute(SubmitterInterface.query_matcher(match_patterns)).scalars().first()
|
12
|
+
if matcher is None:
|
13
|
+
return None
|
14
|
+
SubmitterInterface.add_mission_matchers(session, matcher.mission, match_patterns, matcher.mission.matchers)
|
15
|
+
return matcher.mission
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def _add_tags(session: Session, mission: Union[Mission | None] = None, tags: List[str] = []):
|
19
|
+
exist_tags = session.execute(SubmitterInterface.query_tag(tags)).scalars().all()
|
20
|
+
tags = SubmitterInterface.add_mission_tags(session, mission, tags, exist_tags, mission.tags)
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def match_mission(session: Session, match_patterns: List[str]) -> Mission:
|
24
|
+
mission = SyncSubmitterInterface._query_mission(session, match_patterns)
|
25
|
+
session.commit()
|
26
|
+
return mission
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def create_mission(session: Session, content: str, match_patterns: List[str], tags: List[str] = []):
|
30
|
+
mission = SyncSubmitterInterface._query_mission(session, match_patterns)
|
31
|
+
mission = SubmitterInterface.create_mission(session, content, match_patterns, mission)
|
32
|
+
SyncSubmitterInterface._add_tags(session, mission, tags)
|
33
|
+
session.commit()
|
34
|
+
return mission
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def add_tags(session: Session, match_patterns: List[str], tags: List[str]):
|
38
|
+
mission = SyncSubmitterInterface._query_mission(session, match_patterns)
|
39
|
+
if mission is None:
|
40
|
+
raise ValueError("Mission not found")
|
41
|
+
SyncSubmitterInterface._add_tags(session, mission, tags)
|
42
|
+
session.commit()
|
43
|
+
|
44
|
+
|
45
|
+
class Submitter(SyncSubmitterInterface):
|
46
|
+
def __init__(self, session: Session):
|
47
|
+
self.session = session
|
48
|
+
|
49
|
+
def match_mission(self, match_patterns: List[str]) -> Mission:
|
50
|
+
return SyncSubmitterInterface.match_mission(self.session, match_patterns)
|
51
|
+
|
52
|
+
def create_mission(self, content: str, match_patterns: List[str], tags: List[str] = []):
|
53
|
+
return SyncSubmitterInterface.create_mission(self.session, content, match_patterns, tags)
|
54
|
+
|
55
|
+
def add_tags(self, match_patterns: List[str], tags: List[str]):
|
56
|
+
return SyncSubmitterInterface.add_tags(self.session, match_patterns, tags)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
LICENSE
|
2
|
+
README.md
|
3
|
+
setup.py
|
4
|
+
missionpanel/__init__.py
|
5
|
+
missionpanel.egg-info/PKG-INFO
|
6
|
+
missionpanel.egg-info/SOURCES.txt
|
7
|
+
missionpanel.egg-info/dependency_links.txt
|
8
|
+
missionpanel.egg-info/requires.txt
|
9
|
+
missionpanel.egg-info/top_level.txt
|
10
|
+
missionpanel/handler/__init__.py
|
11
|
+
missionpanel/handler/handler.py
|
12
|
+
missionpanel/orm/__init__.py
|
13
|
+
missionpanel/orm/core.py
|
14
|
+
missionpanel/orm/handler.py
|
15
|
+
missionpanel/submitter/__init__.py
|
16
|
+
missionpanel/submitter/abc.py
|
17
|
+
missionpanel/submitter/asynchronous.py
|
18
|
+
missionpanel/submitter/submitter.py
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
from setuptools import setup
|
4
|
+
from setuptools import find_packages, setup
|
5
5
|
|
6
6
|
with open("README.md", "r", encoding='utf8') as fh:
|
7
7
|
long_description = fh.read()
|
@@ -12,15 +12,15 @@ package_dir = {
|
|
12
12
|
|
13
13
|
setup(
|
14
14
|
name='missionpanel',
|
15
|
-
version='1.0.
|
15
|
+
version='1.0.2',
|
16
16
|
author='yindaheng98',
|
17
17
|
author_email='yindaheng98@gmail.com',
|
18
18
|
url='https://github.com/yindaheng98/missionpanel',
|
19
19
|
description=u'A mission panel',
|
20
20
|
long_description=long_description,
|
21
21
|
long_description_content_type="text/markdown",
|
22
|
-
package_dir=
|
23
|
-
packages=[
|
22
|
+
package_dir={'missionpanel': 'missionpanel'},
|
23
|
+
packages=['missionpanel'] + ["missionpanel." + package for package in find_packages(where="missionpanel")],
|
24
24
|
classifiers=[
|
25
25
|
"Programming Language :: Python :: 3",
|
26
26
|
"License :: OSI Approved :: MIT License",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|