Some big changes today as multiple things are coming together! Most of the work is now in the stages
module, the code for this pots is at commit 0e67f0d
Delay stages
I've decided to instead of using a task queue system for timed events, simply add a DelayStage
quest stage that enacts a time delay. This reduces the complexity by knocking out a piece of infrastructure, and was surprisingly easy to write given the existing infrastructure:
class DelayStage(Stage):
""" For enacting a time delay """
@property
@abstractmethod
def delay(cls) -> timedelta:
""" how much time to delay """
return NotImplemented
def prepare(self) -> None:
""" On first run, insert datetime """
if self.get_stage_data() is None:
now = datetime.now().timestamp()
logger.info(f"Delay stage prepare", now=now)
self.set_stage_data(now)
def condition(self) -> bool:
""" Calculate whether that has elapsed """
target = datetime.utcfromtimestamp(self.get_stage_data(0)) + self.delay
now = datetime.now()
logger.info(f"Delay stage prepare", now=now, target=target)
return now > target
The DelayStage uses the prepare()
event to write into its stage storage (a new free-form dict value that I've added to the quest data storage model to store stage-specific data) the target timestamp, and when the condition runs next, it tests if this time has been reached or exceeded! Very simple.
Creating Issues
Now that we have the code for characters to post github messages, I can add a CreateIssueStage()
that will create a new issue in a user's fork, it looks like this:
class CreateIssueStage(Stage):
""" This stage posts a new issue to a user's fork """
@property
@abstractmethod
def character(cls) -> Character:
""" Which character will post the issue """
return NotImplemented
@property
@abstractmethod
def issue_title(cls) -> str:
""" Issue title """
return NotImplemented
@property
@abstractmethod
def issue_body(cls) -> str:
""" Issue main body """
return NotImplemented
# variable to store resulting issue ID in for later
issue_id_variable: Optional[int] = None
def execute(self) -> None:
""" Post the issue to the fork """
game = self.quest.quest_page.game
game.load()
fork_url = game.data.fork_url
logger.info("Creating issue", title=self.issue_title, fork_url=fork_url)
issue_id = self.character.issue_create(
fork_url, self.issue_title, self.issue_body
)
if self.issue_id_variable:
logger.info(
"Storing issue Id in variable",
issue_id=issue_id,
variable=self.issue_id_variable,
)
setattr(self.quest.quest_data, self.issue_id_variable, issue_id)
It has some abstract properties for the character, issue title, and so on; and the business part of the class simply calls character.isseu_create()
that's part of the package defined last time. The rest of the execute()
event deals with fetching the fork_url, and saving the results in a location of your choosing, so that a stage can output the issue ID created so that other stages can use it.
In usage, it looks like this:
class IntroQuest(Quest):
class QuestDataModel(QuestBaseModel):
issue_id: Optional[str] = None
last_comment: Optional[datetime] = None
version = VersionInfo.parse("0.1.0")
difficulty = Difficulty.BEGINNER
description = "The intro quest"
class Start(CreateIssueStage):
children = ["CheckNumber1"]
character = character_garry
issue_id_variable = "issue_id"
issue_title = "Welcome, can you give me a hand?"
issue_body = """\
Hello therre!
"""
The reason for the """\
is due to the use of pythons dedent method which keeps intedentation nicer in the source code for multi-line strings.
When this Start
event fires, it will use the character defined by character_garry
to send an issue to the player's fork, with the given issue title and body. It will also store the issue_id in the quest data model's issue_id
key, so that next stages can re-use it.
Testing
In order to test, to avoid spamming data somewhere public, I've over-written the test files to fake a fork to a private github repo instead. This usually isn't possible on GitHub during a normal flow, but our system doesn't make a distinction, so happily accepts it as part of a test.
I can run the tests already in the github_webhook_listener
to test the creation of this first issue since it already starts by deleting the test user's game and starting a new one. I can also test follow-on stages using the tests already defined in test_main_tick
Top comments (0)