Database Class

I currently have my own code for generating batches of points and labels as below:

def load_lasfile(filename):
   inFile = laspy.file.File(filename)
   data = np.vstack([inFile.x, inFile.y, inFile.z, inFile.classification]).transpose()
   points = data.astype(np.float32)
   # labels_seg = data.loc[:, 'class'].to_numpy().astype(np.int64)
   return points  # labels_seg

def minMax(x):
   return pd.Series(index=['min', 'max'], data=[x.min(), x.max()])

class Dataset(IterableDataset):
   """
   """

   def __init__(self, file_in, path='../Data/DALES', batch_size=32, npoints=2048):
       super(Dataset).__init__()
       self.file_list = [os.path.join(path, line.strip()) for line in open(os.path.join(path, file_in))]
       self.batch_size = batch_size
       self.npoints = npoints
       self.index = 0
       self.first_batch = True
       self.sq_size = 0
       self.label_to_names = {0: 'unknown',
                              1: 'buildings',
                              2: 'cars',
                              3: 'trucks',
                              4: 'poles',
                              5: 'power lines',
                              6: 'fences',
                              7: 'ground',
                              8: 'chair',
                              9: 'vegetation'}
       self.init_labels()
       self.ignored_labels = np.array([0])
       self.dataset_task = 'segmentation'

   def next_batch(self):
       # __file = self.file_list[self.index]
       for each_file in self.file_list:
           # Find sq_size which gives value of x and y coordinates skip considering uniformity in data files
           batch_accumulation = 0
           data_ = load_lasfile(each_file)
           print("-----------{}-----------".format(each_file))
           if self.first_batch:
               data_min = data_.min(axis=0)
               data_max = data_.max(axis=0)
               x_range = data_max[0] - data_min[0]
               y_range = data_max[1] - data_min[1]
               x_unq = np.sort(np.unique(data_[:, 0]))
               y_unq = np.sort(np.unique(data_[:, 1]))
               size = 0
               while size < self.npoints and self.first_batch:
                   self.sq_size += 1
                   pos_x = np.logical_and(x_unq[self.sq_size] > data_[:, 0],
                                          data_[:, 0] >= x_unq[0])
                   pos_y = np.logical_and(y_unq[self.sq_size] > data_[:, 1],
                                          data_[:, 1] >= y_unq[0])
                   pos = np.logical_and(pos_x, pos_y)
                   size = len(data_[pos])
               self.sq_size -= 1  # to include not more than self.npoints
           self.first_batch = False
           self.y_ll = 0
           while (self.y_ll + self.sq_size) < len(y_unq):
               self.y_ul = self.y_ll + self.sq_size
               self.x_ll = 0
               while (self.x_ll + self.sq_size) < len(x_unq):
                   self.x_ul = self.x_ll + self.sq_size
                   pos_x = np.logical_and(x_unq[self.x_ul] > data_[:, 0],
                                          data_[:, 0] >= x_unq[self.x_ll])
                   pos_y = np.logical_and(y_unq[self.y_ul] > data_[:, 1],
                                          data_[:, 1] >= y_unq[self.y_ll])
                   pos = np.logical_and(pos_x, pos_y)
                   self.x_ll += self.sq_size
                   data = data_[pos]
                   data = np.resize(data, (self.npoints, data_.shape[1]))
                   if labels_seperate:
                       if batch_accumulation < self.batch_size:
                           if batch_accumulation == 0:
                               data_batch = np.reshape(data[:, :-1], (1, data.shape[0], data.shape[1] - 1))
                               labels_batch = np.reshape(data[:, -1], (1, -1))
                           else:
                               data_batch = np.vstack(
                                   (data_batch, data[:, :-1].reshape((1, data.shape[0], data.shape[1] - 1)))
                               )
                               labels_batch = np.vstack(
                                   (labels_batch, data[:, -1].reshape((1, -1)))
                               )
                           batch_accumulation += 1
                       else:
                           batch_accumulation = 0
                           yield data_batch, labels_batch
                   else:
                       if batch_accumulation < self.batch_size:
                           if batch_accumulation == 0:
                               data_batch = np.reshape(data, (1, data.shape[0], data.shape[1]))
                           else:
                               data_batch = np.vstack(
                                   (data_batch, data.reshape((1, data.shape[0], data.shape[1])))
                               )
                           batch_accumulation += 1
                       else:
                           batch_accumulation = 0
                           yield data_batch
               self.y_ll += self.sq_size
           self.y_ul = -1
           self.x_ul = -1
           pos_x = np.logical_and(x_unq[self.x_ul] > data_[:, 0],
                                  data_[:, 0] >= x_unq[self.x_ll])
           pos_y = np.logical_and(y_unq[self.y_ul] > data_[:, 1],
                                  data_[:, 1] >= y_unq[self.y_ll])
           pos = np.logical_and(pos_x, pos_y)
           data = data_[pos]
           data = np.resize(data, (self.npoints, data_.shape[1]))
           if labels_seperate:
               if batch_accumulation < self.batch_size:
                   if batch_accumulation == 0:
                       data_batch = np.reshape(data[:, :-1], (1, data.shape[0], data.shape[1] - 1))
                       labels_batch = np.reshape(data[:, -1], (1, -1))
                   else:
                       data_batch = np.vstack(
                           (data_batch, data[:, :-1].reshape((1, data.shape[0], data.shape[1] - 1)))
                       )
                       labels_batch = np.vstack(
                           (labels_batch, data[:, -1].reshape((1, -1)))
                       )
                   yield data_batch, labels_batch
           else:
               if batch_accumulation < self.batch_size:
                   if batch_accumulation == 0:
                       data_batch = np.reshape(data, (1, data.shape[0], data.shape[1]))
                   else:
                       data_batch = np.vstack(
                           (data_batch, data.reshape((1, data.shape[0], data.shape[1])))
                       )
               yield data_batch

   def init_labels(self):
       # Initialize all label parameters given the label_to_names dict
       self.num_classes = len(self.label_to_names)
       self.label_values = np.sort([k for k, v in self.label_to_names.items()])
       self.label_names = [self.label_to_names[k] for k in self.label_values]
       self.label_to_idx = {l: i for i, l in enumerate(self.label_values)}
       self.name_to_label = {v: k for k, v in self.label_to_names.items()}

   def __iter__(self):
       return self.next_batch()

   def __len__(self):
       return 2000

class Dataset was imported as DalesDataset and used in train file as below.
    training_dataset = DalesDataset(config.train_filelist, path='../Data/DALES/train', batch_size=1, npoints=2048)
    test_dataset = DalesDataset(config.test_filelist, path='../Data/DALES/test', batch_size=1, npoints=2048)

    training_loader = DataLoader(training_dataset,
                                 batch_size=1,
                                 num_workers=0,
                                 pin_memory=True)
    test_loader = DataLoader(test_dataset,
                             batch_size=None,
                             num_workers=0,
                             pin_memory=True)

but I run into many errors. How should the data look to feed into your model?
I did not understand the length for the generator. so I added a random 2000 for checking how it runs.
But I find it hard to integrate this into your code.  Is it possible to use this code or should I make changes?
I just need an idea or a link from where I can make it proper.
Thanks,
Arjun

I just learned PyTorch while coding this repo, so I just followed tutorials on PyTorch website (here or here) and everytime I had a problem, I found a solution on stackoverflow or similar forums.
About the length, it is useless, the code never calls it, so you don't have to worry about it.
I would advise not to use iterable dataset as they are not very versatile. Prefer map style datasets and try to take inspiration from the dataset I already coded like S3DIS.

Hi @HuguesTHOMAS  Thank you for your reply. I have one problem using the S3DIS as example, in it you have mentioned the data is small enough to be loaded into the memory. For me it is not so is it still feasible to use the same structure? Thanks.

If you have a lot of small files (like lidar scans or RGBD images), you can use SemanticKitti dataset as an example. Otherwise if you have large scenes it is going to be much more difficult for you because I did not code anything for that case.
I kinda thought of this problem and here is my advise on it. If you have a lot of very large scenes, you need to break down your dataset in small tiles (each tile in a separate file) so that you can load the points fast. Then when you need a new input cloud (lets imagine a sphere like I do in S3DIS), you need to compute which tiles your sphere intersect, load them, and then get the points in the sphere. This will not be simple to implement but it should be good to keep a fast training.

Okay, Mine is Lidar point cloud which have in total of 0.5 B points but it is tiled into smaller squares. I think I need to understand your S3DIS code more. Anyways, do you find using spherical neighbors highly impacting accuracy? I thought of making small square area with input points just more than ( let's say ) 4096 points and then reduce the size to 4096 to make it equal for all iterations. I read that (from the paper) you have implemented something pretty good to consider the variation in the input points. I am yet to find that snip of your code. Or is it such that I don't have to create a stable number of input points. The network would adapt to it?
Thank you so much for taking your time to reply my questions

Or is it such that I don't have to create a stable number of input points. The network would adapt to it?

Yes it is like that.
First the network does not need a constant number of point as input like Pointnet. The input number of points is variable. It is possible because we use "stacked" batch instead of regular ones. We don't have a batch with shape [B, N, ...] but we stack all clouds like this: [N1 + N2 + N3, ...], therefore N1, N2, and N3 do not have to be the same.
Second, to have a relatively stable memory consumption, the network automatically adapts the batch size depending on the size of the input clouds. Let's say you ask for batch_num = 8. The network is not going to always use 8 input clouds in each batch. Instead it will add clouds to the batch until it reaches a certain limit N_max. Sometimes the batch will contain 5 big clouds, and some other times it will contain 10 smaller clouds but in average it will be around 8. You can see this in the calibration function of S3DIS dataset:
https://github.com/HuguesTHOMAS/KPConv-PyTorch/blob/94b1d87e17ec339e2aad049ce5d5d175b9cd0db1/datasets/S3DIS.py#L1038

@Arjun-NA Hello, has your problem been solved? I found the same dataset, but didn't know how to train the model?

@xdw-123  Hi , I used tensorflow version for now, in which NPM3d dataset code is easily convertible to this dataset. If I have any progress with pytorch I will update it soon here.

@xdw-123 Hi , I used tensorflow version for now, in which NPM3d dataset code is easily convertible to this dataset. If I have any progress with pytorch I will update it soon here.

Thank you for your reply. Do you mean that dataset DALES can be converted to this required dataset format through NPM3d dataset code?

@xdw-123  You can download the DALES in ply format which is available with the mail they send you

This location can be then set in the database file. no need of any other conversions.

@xdw-123 You can download the DALES in ply format which is available with the mail they send you

This location can be then set in the database file. no need of any other conversions.

Thank you for your reply. I'll try it right away.There are three formats for the same dataset in the pictures you send. Do you want only one?Is it necessary to have all three?

@Arjun-NA Hello, I tried running Python 3.5 training_NPM3D.py.Many problems have arisen.But most of them are read and write errors in ply files. Can you upload your modifications?

@xdw-123 sorry for the late reply.
I missed out that the ply file that they provide is in ascii format and you would need in binary format. I will upload the conversion file in here.

Hello @Arjun-NA, @xdw-123 , I've managed to train RandLA-Net on DALES with this, I'm currently investigating what do I need to change in this dataset to fit KPConv. I'm assuming you could use it too for the DALES dataset.
Best Regards,

Hello @Arjun-NA, @xdw-123 , I've managed to train RandLA-Net on DALES with this, I'm currently investigating what do I need to change in this dataset to fit KPConv. I'm assuming you could use it too for the DALES dataset.
Best Regards,

Hi @eyalol , I am not sure about the dataset RandLA-Net, please check with Dr. Hughes for more information. Thanks


Opening the profile does not work if the default browser is Safari

If the default browser is Safari, or if the profiler URL is manually loaded in Safari, the profile will not be displayed. Instead, the Firefox profiler will show an error message saying that the request failed.
This is due to https://github.com/firefox-devtools/profiler/issues/3446 .

Or, more precisely, due to https://bugs.webkit.org/show_bug.cgi?id=171934.


Fix: Show 'Saved!' snackbar only on manual save, not on auto-save

Description:
This pull request addresses an issue where the "Saved!" snackbar would appear every time changes were made to the input fields, even if the user hadn't explicitly clicked the "Save" button. The snackbar should only display when the user performs a manual save action.
Changes Made:

Manual vs Auto Save Handling:

Added a manualSave flag to distinguish between manual saves (via the "Save" button) and auto-saves (triggered by input changes).
Updated the saveAllLinks function to accept a parameter (isManual) that determines whether to show the "Saved!" snackbar or not.

Prevent Snackbar on Auto-Save:

Adjusted the logic inside the saveAllLinks function to prevent the "Saved!" snackbar from appearing when the auto-save feature is enabled.

Save Button Event Listener:

The "Save" button event now triggers the snackbar message correctly after saving.

Custom Link Deletion:

Ensured that when a custom link is deleted, the auto-save mechanism saves the state without triggering the snackbar.

Thanks for contributing! A great idea, indeed it's kinda annoying to keep popping 'saved' on auto-save. However, we don't need an extra boolean variable to keep track of this so I just use if (!autoSaveButton.classList.contains('auto-save-enabled')) showSnackbar('Saved!')


Requests for donations from within the vi editor (accessed through codespaces)

The "Become a registered Vim user!" line on the intro screen when using vi from the codespaces terminal (launching the editor by just typing vi) displayed a non-specific request for donations for the "poor children in Uganda" when I launched the editor earlier today.
Version: 1.79.2
Commit: 695af097c7bd098fbf017ce3ac85e09bbc5dda06
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4.1 Safari/605.1.15
Embedder: codespaces

Please use the Start Extension Bisect command to investigate whether the problem is being caused by one of your extensions.

Here's a screenshot of the line on a fresh codespaces instantiation using the codespaces-jupyter template on github.com:

@Werner0 did you consider to report this to the VIM project? I think what you see there is not associated to VS Code or Codespaces.

@bpasero I have not considered reporting the issue to the VIM project yet. However, I have so far only seen the edited VIM screen on the Jupyter Notebook (codespaces-jupyter) template, not the blank codespaces template. These templates are provided by Github.


Copying of cell output suppressed by user-select:none

In the classical notebook it was possible to copy cell output by selecting it with the cursor.
However this is no longer possible since the class of the output cell p-Widget jp-Output-result jp-Output-executeResult  prevents it specifically user-select: none and cursor.
.p-Widget {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  cursor: default;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

I got the last sources from master repo right now and can't see this issue.
Actually
an .jp-Output-result.p-Widget selector override user-select: properties into "text".
I have checked with last google-chrome and firefox 39. Works ok, I can copy text from output cell.
Only difference with Notebook is cursor property, in Lab it's pointer and in classical Notebook it's cursor: text
It can be fixed by adding "cursor: text" into .jp-Output-executeResult class.
Can I do PR?

@Dumbris, we are planning to address this, but are currently blocked, see #355.


Fix writing pump_probe information from the command line.

At the moment it gives the following error:
Sorry: Uninterpretable command line argument: 'pump_probe.pump_status=True'
It's very likely a problem with the phil parser ...

Now fixed in version 0.6.2.


Most efficient way to check if ICP found the right transformation between two clouds

Hi !
I was wondering what was the best way to say if the step of the ICP achieved to get the "right" transformation in order to merge 2 point cloud ?
I saw here some posts, so I computed the mean distance and/or the hausdorff distance after the ICP step.

https://github.com/ethz-asl/libpointmatcher/blob/master/examples/icp_advance_api.cpp

My guess was : if after the ICP step, two pointcloud get a distance approaching 0 the ICP went well, is that right ?
But is this the best way ? Can i have your thoughts on this please ?
Thank you for your time :)

That will most depend if you identified the outliers correctly. This might be tricky depending of your application.

I have a Velodyne VLP16 that i just want to move through a room in order to scan it.
I also use a Inertial Measurement Unit which gives me the delta of the orientation between two scans.
In my case i could use the orientation to know if my scan went well.
And lets take the case where the outliers are removed correctly.

ICP will minimize an alignment error, that how it was designed. Typical pitfalls are:

outliers drag the local minima somewhere else
the environment is under constrained (i.e., two planes can slide over each other)
sensor sampling is not uniform in the Euclidian space

The second point can happen often and won't be detected with a small residual error.

I understand that errors could happen.
Since my tests (with mean distance and/or the hausdorff distance )
I have seen that a more accurate distance can be computed with the function getResidualError. Is that correct ?
I saw it in the issue :  https://github.com/ethz-asl/libpointmatcher/issues/154
My problem is that the function is not known by the compiler, do i have to include some additional files ?
And if it's possible for you, can you please write/send me an example of code/pseudo code where you compute that use the residual Error function.
Thank you :)

The functions are clearely implemented (ex.: https://github.com/ethz-asl/libpointmatcher/blob/master/pointmatcher/ErrorMinimizersImpl.cpp#L120), but you need to feed some arguments.
Those functionalities were added by other users, so I cannot help much with the reste. Once I've access to more resources (human or time), I'll have to trigger a cleanup of the ErrorMinimizer files as it see to have drifted over time.

Check issue #193 for an example.


The intrinsic size checks were very slow

Now we run them once per render object.
Fixes #2487

@Hixie


khepri_path: Call realpath/1 in pattern_includes_root_node/1

This cleans any use of ?ROOT_NODE or ?THIS_NODE in the path. Otherwise the function would return a different result if these macros were prepended to ?STAR or ?STAR_STAR for instance, even though the meaning of the path is exactly the same.
I'm still not very happy with this heuristic to determine if the root node properties should be included in a get. Another solution would be to require an explicit ?ROOT_NODE in the path to include its properties in the result.

Pull Request Test Coverage Report for Build 156

0 of 4 (100.0%)  changed or added relevant lines in 1 file are covered.
No unchanged relevant lines lost coverage.
Overall coverage decreased (-0.3%) to 80.981%

Totals

Change from base Build 153:
-0.3%

Covered Lines:
1222

Relevant Lines:
1509

💛  - Coveralls

Pull Request Test Coverage Report for Build 156

4 of 4 (100.0%)  changed or added relevant lines in 1 file are covered.
No unchanged relevant lines lost coverage.
Overall coverage decreased (-0.3%) to 80.981%

Totals

Change from base Build 153:
-0.3%

Covered Lines:
1222

Relevant Lines:
1509

💛  - Coveralls


Constrain digestif version

The CI is failing on master following an update of digestif. That digestif update enabled unit tests on the s390x architecture, which are the ones to fail.

Note that digestif.1.0.1 works on s390x, if something does not work on big-endian architecture, it's more about irmin (or a certain use of ocaml-git) than digestif, see:

https://github.com/mirage/digestif/pull/113
https://github.com/mirage/checkseum/pull/55
https://github.com/mirage/decompress/pull/131

Note that the irmin-git UTs aren't the ones that fail the CI.
This one segfaults: https://github.com/mirage/irmin/blob/master/test/irmin/test_tree.ml#L423
This on fails with an incorrect hash: https://github.com/mirage/irmin/blob/master/test/irmin/test_hash.ml#L19
┌──────────────────────────────────────────────────────────────────────────────┐
│ [FAIL]        hash          0   short_hash.                                  │
└──────────────────────────────────────────────────────────────────────────────┘
ASSERT Specialised short hash
FAIL Specialised short hash

   Expected: `241225442164632184'
   Received: `-546621143784531709'

Can you add more details in the commit message and PR body? Keeping track of why stuff are constrained that way is always super so any hints are always useful.

And an issue in digestif will be perfect 👍

The short_hash error can be fixed by adapting the literal hash to big endian architectures.

I'll investigate the segfault problem next

The short_hash error can be fixed by adapting the literal hash to big endian architectures.

In that case, I suspect that the error is here:
https://github.com/mirage/irmin/blob/791badb74962032895c1f8505b33a4896a76d978/src/irmin/hash.ml#L24

Good catch.
I was referring to that line directly in the unit tests,
https://github.com/mirage/irmin/blob/master/test/irmin/test_hash.ml#L25

Closing, as this is not a problem with digestif


Update SSIS 2019 for Linux doc

Update SSIS 2019 for Linux doc with right repository

#sign-off

@yidetu: I'm sorry - only the author of this article, @lrtoyou1223, can sign off on your changes. But we do have an exception process - if you are on the Microsoft content or product team for this product area, you can ask the PR review team to review and merge it by sending mail to the techdocprs alias.

Hello @rothja - Would you review the proposed updates and indicate whether the PR can be merged? Thanks!

@yidetu : Thanks for your contribution! The author(s) have been notified to review your proposed change.

@yidetu Thank you for catching this and letting us know. However, I think we have a bigger change to make here, because the article needs to work for both 2017 and 2019 content based on the version selector on docs. The easiest way is to just instruct the user to first install SQL Server. Based on their version, they would have configured the appropriate repo. I'm going to add some suggestions for this change to your PR and you can accept them and still remain the contributor on this request. Thank you! @Vanmsft FYI

@yidetu : Thanks for your contribution! The author(s) have been notified to review your proposed change.

Jason, thanks for reviewing the change. I didn't aware that this doc is for both versions.
You suggested change is not ideal.  Install SQL engine for Linux is not a prerequisite of install SSIS. So it's not reasonable to put install SQL Server as first step.
I've updated the doc with a moniker switch. Please help to take a look.

@yidetu Your change looks good. But I suggest adding one moniker to the 2019 condition in case the user selects "all versions" in the filter list. In that case, I think showing the 2019 example makes sense rather than both lines which would be potentially confusing:
::: moniker range="= sql-server-linux-2019 || = sql-server-2019 || = sqlallproducts-allversions"

Can you make that change to both 2019 sections? Then I think this is great. Thank you!

@yidetu : Thanks for your contribution! The author(s) have been notified to review your proposed change.

Thanks for the suggestion, updated.

@yidetu Perfect. Thanks for patiently working with me through this. #sign-off @vanmsft FYI.

@rothja: I'm sorry - only the author of this article, @lrtoyou1223, can sign off on your changes. But we do have an exception process - if you are on the Microsoft content or product team for this product area, you can ask the PR review team to review and merge it by sending mail to the techdocprs alias.

@rothja @yidetu Jason & Yide, there is no such Versioning moniker named 'sql-server-2019', thus PR 13294 in the public repo 'sql-docs-pr' has failed.
You must mean moniker 'sql-server-ver15' (as in SELECT @@VERSION returns a value of "15.whatever".

I've updated to "::: moniker range=">= sql-server-linux-ver15 || >= sql-server-ver15 || =sqlallproducts-allversions"", thanks Gene.

@MightyPen I'm sorry I didn't catch that when reviewing.


Const definitions with arithmetic operations fail in verus! macro

Only happens in the new verus! macro. Arithmetic operations in const definitions promote the number to nat or int.
#[allow(unused_imports)]
use builtin::*;
use builtin_macros::*;

verus! {

const X: usize = 10 * 10;

fn main() {
}
}

fhs m: source ν ./tools/rust-verify.sh /st/verus/test.rs
error[E0308]: mismatched types
 --> /st/verus/test.rs:7:18
  |
7 | const X: usize = 10 * 10;
  |                  ^^^^^^^ expected `usize`, found struct `builtin::nat`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
verification results:: verified: 0 errors: 0

cc @Chris-Hawblitzel

Should be fixed by 114a4b4cee7337bcaa67b6923b75ae0083674503

I'm going to close this, @matthias-brun feel free to reopen if you need to


Microsoft logo stylised and topics added.

microsoft in the description stylised in italics and added topics that relate with vscode.

Thank you for the PR, but in this context 'Microsoft' and 'Visual Studio Code' should not be shown in italics.
Your change to 'Visual Studio Code' breaks that the words are rendered as a link. To show a link in italics use
*[Visual Studio Code](https://code.visualstudio.com)* instead of *[Visual Studio Code]* (https://code.visualstudio.com).

Im a begineer , its great to hear from people in microsoft.
I will take care of that in my future contributions .
Regards ,
SurajK.
On Tue, Oct 8, 2019, 7:18 PM Erich Gamma<EMAIL_ADDRESS>wrote:

Closed #82064 https://github.com/microsoft/vscode/pull/82064.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/microsoft/vscode/pull/82064?email_source=notifications&email_token=AKQS5CUEYH4ILNXZS27XPNTQNSFTTA5CNFSM4I6JUNT2YY3PNVWWK3TUL52HS4DFWZEXG43VMVCXMZLOORHG65DJMZUWGYLUNFXW5KTDN5WW2ZLOORPWSZGOUCU2XAQ#event-2695474050,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AKQS5CU6HE3W22IOHKJG4NTQNSFTTANCNFSM4I6JUNTQ
.


[3.0] Money value object

So the de-facto package seems to be this: https://github.com/mathiasverraes/money
Work is happening on the nextrelease branch: https://github.com/mathiasverraes/money/tree/nextrelease
Money has as static 'from string'  method to intialise from string, but we should encourage setting an integer. This could also be used for currency.
I've asked for the timeframe for 3.0 is, so we might be able to use that.

See https://github.com/mathiasverraes/money/issues/137#issuecomment-173925700
It's close enough I guess :)

Money has as static 'from string' method to intialise from string

I think @frederikbosch is working on a money parser for greater flexibility, so one can use that as well.

but we should encourage setting an integer

Actually this is not a restriction anymore from an exact type point of view. You can pass the number as a string as well (allows using BCMath and GMP with big numbers), but it still has to be an integer representation.

@sagikazarmark Right, the static method is going to disappear and will be replaced by a parser interface. The parser is accompanied by its sibling formatter interface.

I've noticed the parser does a cast to (string) in at least one constructor. Doing that raised this unexpected issue for us, so I've raised it as an issue
We don't want old issues coming back when we shed legacy code for a third party pachage. Talking of which, I need to get that fix pushed, but was hoping for some feedback first, to make sure I haven't overlooked something important.

So I've looked in Money, and the best way to handle this is to just use integers as amount. So instead of '10.00', supply 1000. But that's a pretty BC break.
It would be probably best, so we can avoid string/float/integer conversions altogether.
And it would be best to use the Currency also, but it would need the numeric code.
I've already implemented an external currency list; https://github.com/thephpleague/omnipay-common/commit/af429996b24cb8543d057c05841b2205208af98e
So perhaps Money would also do something similar.

And the more I think about it, the Money package is probably not really needed. It does add value for parsing strings to float, formatting float to strings and making calculations with the amount. But we just need to store a value, do some validity checks and then return the value again. Depending on the gateway, it needs to be converted again.
So why not:

setAmount() accepts an integer. This is used for checks etc.
getAmount() returns the same (unmodified) integer.

And perhaps add a method to convert the amount to float/string, using the currency decimals?

I am not entirely sure about your use case of handling monetary value, but believe me: it is not that simple. :) Let me mention some points from the lifecycle of Money:
This library we are talking about started exactly here: store money as integer. There are several issues with it, that's why we started to store money internally as string. To name a few:

rounding
subunits
conversion from float/string
. storing numbers which overflow the integer boundary

These are all problematic things you have to care about.

If we only need storing and returning the amount, I would also opt for using strings. I agree with @sagikazarmark that conversion is always accompanied with some problems and in the end, the value is always transfered as string to the payment gateway and never as int, float or something else.

The idea of the money object is the recognition that an amount is more than just a number. The currency is an important part of that - the two go together and should stay together.
For example, you cannot just send that integer to most gateways without formatting it. You cannot format that integer without knowing the currency it represents, and that is driven by the metadata that goes along with that currency.
With a money object, the amount and currency are always kept together. Each gateway then knows exactly what it is dealing with, and it has the tools there to format it as it needs to. Applications passing amounts into OmniPay also then have a consistent interface to use, and don't have to do their own conversions to other formats.

One more thing: if the basket/cart is expanded to offer the flexibility that some gateways offer, then we will need to start doing some money arithmetic, which is something OmniPay does not do at all at this time. Once we get into that, to make sure it goes smoothly, we need all the help we can by encapsulating that into a functions that already know about rounding, currency arithmetic, sub-units etc.

Yes but for Omnipay, rounding/subunits aren't really a concern, because no actual changes are made. Omnipay just uses the amount parameter to store the value and retrieve it again.
Some gateways use integers, other floats/strings, so some conversion is always needed, it only differs what we take as base value.
And yes, the Amount and currency are bound, but most gateways don' require the amount formatted with currency right? And Money doesn't seem to just transform an integer to a float, based on the currency, but also adds the currency sign. The other way around, developer don't usually input amounts with the currency sign, right?
So now we have to methods:

getAmount() -> parses the amount to a float, validate it, parse to a string
getAmountInteger() -> Use the getAmount() result and apply currency factor.

With not just store as integer from the first place? If we use Money, I would argue that we would still best provide an integer as the input. But then it doesn't have much added value, or I'm missing something important.
The currency in Omnipay is currently already combined, so could be something like this:
public function getAmountFloat()
{
    return $this->getAmount() / $this->getCurrencyDecimalFactor();
}

@sagikazarmark: There are several issues with it, that's why we started to store money internally as string

Yes, you're storing it as a string internally, but you are forcing an integer as input (in cents). That's what I'm suggesting here also.

Your valid point is that a number is nothing without its currency. A number alone simply has no meaning and the gateways always need the number and the associated currency code.
But payment gateways expect numbers to be always in the same format (e.g. 1000.00) and not according to the language/country conventions the currency is used (e.g. "1.000,00 €" in German speaking countries). Thus, we still vote for storing currency and together with the ISO currency code, but both as strings and avoid conversion by accepting strings as parameters.
We've seen the problems of tax calculation ourselves and I think that shouldn't be done in Omnipay. Just keep it simple because as soon as you calculate taxes, you will have to store values with more precision than two decimals and the problem of rounding errors will still occur (the +-1 cent problem).

I'm not concerned about the currency symbol when formatting. It is more about whether "100" needs to be sent as "10000", "100.00" or "100". ALL of those are a valid representation of £100 in different gateways (though that last one is an edge-case).
Okay, so putting tac caclulations aside, the basket does already multiple item costs by the amount to get the line total. And at least one gateway does need the basket total to be included, which MUST add up to the total of the lines.

Just curious what is the main objection to using a money class? Is it felt to be too big a BC break, or an unnecessary complication? For me, it is an abstraction that fits well with the ways other libraries are moving, and I've had too many very close calls due to PHP float handling and rounding, which I believe highlights it's more complex than just a few scalar values.

Yes, you're storing it as a string internally, but you are forcing an integer as input (in cents).

Yes we do, but keep in mind that we do this to allow integer type overflow. So the representation might be integer, but the type cannot always be.
My point was that there are many problematic things with money handling, which you might not want to care about in Omnipay.

Money accepts an integer and returns one. So when we want to allow a string to be used as input, this needs to be converted anyways. (But money has a parser for that, which may be more or less reliable for our use case.
If we want to output a string, we would still need to convert it ourself.
And without calculations, the money object can be pretty much replaced with the integer directly.
If developers want to use Money, they still scan, and just pass the amount to Omnipay.
If you mean we could just pass a Money object instead of amount and currency, that would be okay with a simple instanceof check and getting the actual value.

But I've tried to use the money library in Omnipay, just wasn't happy with the results because of the mentioned reasons. But feel free to make a PR to make the idea more clear.

@judgej
For me it seems like an unnecessary complication of something that should be best stored in two strings. If you wrap a small class around with two get/set() methods, it would be OK but still without additional value in my point of view.
The valid representations you are mentioning are gateway specific. We shouldn't try to offer all kind of representations but only a standard one (100.00) and leave it up to the Omnipay driver to do the right thing.
Payment gateways ALWAYS need the basket total that should be charged. All other details (product prices, shipping, etc.) are only nice to see at the payment gateway but unnecessary. If you leave the price and tax calculation to the gateway, the sum will differ by one cent in many cases (see PayPal).
Furthermore, Omnipay shouldn't offer a basket implementation by default. That's not the purpose of the common payment gateway interface. It's OK to offer that as additional library but Omnipay itself shouldn't depend on that.

I am talking about the basket object used to provide metadata to the payment gateway in support of the payment, not anything to do with the "basket" used in the application as a part of the customer's shopping experience. Not sure if I made that clear. As such, it is very much in OmniPay's realm to handle how that gets sent to the gateway.

Money accepts an integer and returns one. So when we want to allow a string to be used as input, this needs to be converted anyways.

I am kind of confused what we really are talking about. Money object accepts string and integer as well. It returns string to be consistent about return types. As soon as you pass PHP_INT_MAX, you cannot represent your value as integer. You either need float or string. Money handles all these conversion stuff when possible, in every other cases, it throws an exception for example.
For me, using a custom representation seems to be a bigger complication:

Using Money:

Money->Omnipay->Money

Using custom money object

Custom Money->Omnipay->Custom Money
(but in this case you have to handle all the conversion stuff Money already does)

Using string/integer

String/Integer->Conversion/Overflow check/Rounding/Etc->Omnipay->String/Integer
Maybe I don't fully understand your use case, but from a public API point of view, using a Money VO seems to be a better choice than using string/integer. But that's my point of view.

Whether Omnipay is going to use the Money package as originally created by Mathias Verraes, frankly I don't care. But one should take the right things into account before making decisions. What to take into account.

It must not be possible to change the value of an amount. Driver may not change the value. It is only used to forward it to respective gateway.
The representation of the value might differ per gateway. It should be possible to format the amount as such that it can be accepted by the gateway.
It is possibly wise to store the amount as integer. When people do calculations with the amount, either inside or outside the Omnipay package, they must learn that using floats is very risky. The fact that Omnipay might not be doing calculations with the amount is irrelevant.
An amount will be forwarded to the gateway together with the currency. That is quite logical because an amount only has meaning with a currency. So there multiple reasons to store an amount together with the currency.

What do these considerations learn us?

We need an immutable object that protects the amount to be changed: a value object that represents Money. So please, so no setters.
We need a formatter class that formats the amount to the format as demanded by the gateway.
The value object should only accept integers when constructing the object, should probably use an integer internally and when you need the amount (get) it should return an integer.
The value object should consist of a value and a currency.

Now, before deciding which package you are going to require, one should agree on the requirements. But if you ask me, I think it would be wise to use the Money package that is already there.

@frederikbosch We object against using integers as base representation due to what @sagikazarmark said about overflows (more worse than using floats). A formatter class for a few formats doesn't help too. The rest is simply a matter of style and can be implemented as the majority will like it most.

@aimeos I dont agree with any of your arguments.

@frederikbosch I agree with your analysis, with perhaps the exception of this (maybe):

The value object should only accept integers when constructing the object

The conversion between data types and formats is the thing that can be problematic, so it makes sense to be able to create the object from other formats too. Whether that is built into the money class or are separate helper functions, is less important. What is important is that there is one place where this is done, instead of thirty drivers all doing it their own way. The application will have amounts as integers, strings, floats, with or without decimals or decimal points, and IMO they need to be able to throw it at OmniPay (through an object or helper function) without the need to reinvent this.

I think there is a fair bit confusion about the scope of this issue. For example, the issues with string vs integer vs float; there are 2 issues:

How to store them internally. Integers + float can be stored as strings internally.
If we use decimals by default (eg. 10.00) or integers (whole numbers) for the cents (eg. 1000).

What I mean to say is that a lot of issues with floats/rounding can be prevented by using integers as input (either as actual integer, or a string representing an integer). You don't have precision problems, because you use whole numbers. Only when converting to float, you go from integers to a float (factor 100 usually), so that shouldn't lead to rounding problems.
Current getAmount in  in AbstractRequest. (v2)
/**
* Validates and returns the formated amount.
*
* @throws InvalidRequestException on any validation failure.
* @return string The amount formatted to the correct number of decimal places for the selected currency.
*/
public function getAmount()
{
   $amount = $this->getParameter('amount');
   if ($amount !== null) {
       // Don't allow integers for currencies that support decimals.
       // This is for legacy reasons - upgrades from v0.9
       if ($this->getCurrencyDecimalPlaces() > 0) {
           if (is_int($amount) || (is_string($amount) && false === strpos((string) $amount, '.'))) {
               throw new InvalidRequestException(
                   'Please specify amount as a string or float, '
                   . 'with decimal places (e.g. \'10.00\' to represent $10.00).'
               );
           };
       }
       $amount = $this->toFloat($amount);
       // Check for a negative amount.
       if (!$this->negativeAmountAllowed && $amount < 0) {
           throw new InvalidRequestException('A negative amount is not allowed.');
       }
       // Check for a zero amount.
       if (!$this->zeroAmountAllowed && $amount === 0.0) {
           throw new InvalidRequestException('A zero amount is not allowed.');
       }
       // Check for rounding that may occur if too many significant decimal digits are supplied.
       $decimal_count = strlen(substr(strrchr((string)$amount, '.'), 1));
       if ($decimal_count > $this->getCurrencyDecimalPlaces()) {
           throw new InvalidRequestException('Amount precision is too high for currency.');
       }
       return $this->formatCurrency($amount);
   }
}
/**
 * Get the payment amount as an integer.
 *
 * @return integer
 */
public function getAmountInteger()
{
    return (int) round($this->getAmount() * $this->getCurrencyDecimalFactor());
}

So, if we say, from v3 onwards, input the amount as smalles unit ('cents'), we could use Money in something like this:
(So
/**
  * Validates and returns  amount as integer.
  *
  * @throws InvalidRequestException on any validation failure.
  * @return string The amount in smallest unit possible (eg. 'cents')
  */
 public function getAmount()
 {
     $amount = $this->getParameter('amount');

     if ($amount !== null) {
         $currency = new \Money\Currency($this->getCurrency() ?: 'USD');
         $money = new Money($amount, $currency);

         // Check for a negative amount.
         if (!$this->negativeAmountAllowed && $money->isNegative()) {
             throw new InvalidRequestException('A negative amount is not allowed.');
         }
         // Check for a zero amount.
         if (!$this->zeroAmountAllowed && $money->isZero()) {
             throw new InvalidRequestException('A zero amount is not allowed.');
         }

         return $money->getAmount();
     }
 }

 /**
  * Validates and returns the formatted amount as a float.
  *
  * @throws InvalidRequestException on any validation failure.
  * @return string The amount formatted to the correct number of decimal places for the selected currency.
  */
 public function getAmountFloat()
 {
     $amount = $this->getAmount();
     if ($amount !== null) {
         return (string) ($amount / $this->getCurrencyDecimalFactor());
     }
 }

So you can see, we don't need to modify or calculate anything. We could roughly do the same without converting to Money:
/**
 * Validates and returns  amount as integer.
 *
 * @throws InvalidRequestException on any validation failure.
 * @return string The amount in smallest unit possible (eg. 'cents')
 */
public function getAmount()
{
    $amount = $this->getParameter('amount');

    if ($amount !== null) {
        if (filter_var($amount, FILTER_VALIDATE_INT) === false) {
            throw new InvalidRequestException('Amount must be an integer');
        }
        // Check for a negative amount.
        if (!$this->negativeAmountAllowed && $amount < 0) {
            throw new InvalidRequestException('A negative amount is not allowed.');
        }
        // Check for a zero amount.
        if (!$this->zeroAmountAllowed && $amount == 0) {
            throw new InvalidRequestException('A zero amount is not allowed.');
        }
        return (string) $amount;
    }
}

/**
 * Validates and returns the formatted amount as a float.
 *
 * @throws InvalidRequestException on any validation failure.
 * @return string The amount formatted to the correct number of decimal places for the selected currency.
 */
public function getAmountFloat()
{
    $amount = $this->getAmount();
    if ($amount !== null) {
        return (string) ($amount / $this->getCurrencyDecimalFactor());
    }
}

@judgej I agree with you on the fact that Omnipay should offer an api that allows multiple methods to create an amount.
While I feel that people should learn to use an integer for money instead of float, it's maybe not realistic to require that. Having multiple methods for creating a money object, would be very helpful.
However, that process (parsing) should be separated from the resulting value object. You could create a parser per method, like FloatToMoneyParser and DecimalStringToMoneyParser. That would be in line SRP. A second way could be to create an amount factory, with methods like.

fromFloat(amount, currencyCode)
fromDecimalString(amount, currencyCode)
fromInteger(amount, currencyCode)

I would just stick with 1 input (integer), and 2 output methods (integer and float)

@barryvdh But in that method you are not using money as a value object. You use it as validation tool. Then the third example gives less overhead, and would be prefered over the second with money. I thought you were considering using Money as value object, being the class that is used for amounts and passed through all methods of Omnipay, whether it is common or driver. Too bad, I hope that you will reconsider.

Well yes, that's a different case. But how would you see that? A setMoney attribute where you can provide a money attribute, and it will set the amount and currency parameter? I'm in favor of that, wouldn't even need to require the money package by default.

@barryvdh That is easy.
$request = $gateway->puchase([
    'amount' => new Money(500, new Currency('USD'))
]);

@barryvdh And if I may do more suggestions
$request = $gateway->puchase(new Money(500, new Currency('USD')))
   ->withDescription('..')
   ->withReturnUrl('..')
   ->withNotifyUrl('..')
;

So would that differ much from what we have now?
$request = $gateway->purchase([
    'amount' => 500,
    'currency' => 'USD',
]);

As said, we could add a setter like this:
public function setMoney($value)
    {
        if (!($value instanceof Money)) {
            throw new InvalidRequestException('Not a valid Money object.');
        }

        $this->setAmount($value->getAmount());
        $this->setCurrency($value->getCurrency()->getCode());
    }

$request = $gateway->purchase([
    'money' => new Money(500, new Currency('USD'))
]);

Which would me most useful for when the application is already using the Money object.
I do like the chained setup also :)
@sagikazarmark @frederikbosch
So regardless of the Money class itself, do you suggest we use the integer value (as string) as source, and convert to float when needed? (So the opposite of what we do now)

So this is what I mean, in a PR: https://github.com/thephpleague/omnipay-common/pull/79

@barryvdh If you choose to encapsulate it into an own defined value object, the internal value is not an issue any more. It can be abstracted away easily!
With that object, the api consumer constructs it like this.
$request = $gateway->purchase([
    'amount' => Money::fromInteger(512)
]);
$request = $gateway->purchase([
    'amount' => Money::fromFloat(5.12)
]);
$request = $gateway->purchase([
    'amount' => Money::fromDecimalString('5.12')
]);

Whereas a driver maintainer, will do something similar as this.
$data['amount'] = $this->amount->toInteger();
$data['amount'] = $this->amount->toFloat();
$data['amount'] = $this->amount->toDecimalString();

And now nobody cares how you internally store the amount within the value object. Moreover, you can add methods to your own Money object that are related to the Omnipay package. So let's do that, and go one step further.
Now the api consumer does the following.
$request = $gateway->purchase([
    'amount' => Money::fromInteger(512, 'EUR')
]);

And the driver maintainer does the following.
if ($this->amount->isCurrencyAvailableWithin(['EUR', 'USD']) === false) {
  throw new InvalidArgumentException('Sorry this driver cannot process this currency');
}

That does makes sense, right? Maybe Omnipay does not need all the methods from moneyphp. But it sure needs encapsulating money related behaviour into a value object.

Well, yes, we saw this whole thing from completely different points of view.
I think using Money library would only make sense if you make it part of the public API. That will restrict the user to pass in a valid Money object. All of the conversion/representation issues would just magically go away.
Also, using the Money object internally is quite easy I think.
If you want to allow string/integer values to be passed, then there are many issues you have to face with and Money would probably be an unnecessary complication (as @frederikbosch said above, it would just validate the amount).
Also, @barryvdh I am not sure I understand why you validate the input in a getter. Input should be validated at the injection point and should agressively react if invalid data is passed (like good little Exception to make the developer happier).
Update: ah, @frederikbosch mentioned almost the same things I did. ;)

That's what I thought we were talking about too - using an object to handle the amount all the way through. If the money object handles its own validation, then all OmniPay would need to worry about was that an object of the correct interface was being passed in, and that's it.
The amount can be a third party money object, or it could be a much simpler interface (OmniPay only needs to care about getting values out of the object in a limited number of formats, at this stage at least) so an adapter to a money library would provide the functionality behind it.

@judgej Exactly my thoughts!

@sagikazarmark the input validation is from the already existing code, I just modified that to show the changes.
I do agree that the amount could probably be better of in it's own class, instead of part of a big parameter bag, although is does pretty much the same, but not isolated.
I'm not so sure of providing multiple input formats though.

I'm not so sure of providing multiple input formats though.

That's my point: having a single class enforces the user to pass a strict format.

That was more a response to the 'Money::fromDecimalString' example :)
But I'll see, something in line with the Credit Card etc could work, if string/integer, convert to value object, otherwise use that directly. And create some validation/output of formats there.

Here is a practical example that I hope helps to demonstrate the idea.
I wrote this Amount class last year for a gateway. It has some fancy ways of initialising it to suite different use-cases:
// All £9.99
$amount = Amount::GBP()->withMajorUnit(9.99); // Or "9.99"
$amount = Amount::GBP()->withMinorUnit(999);
$amount = new Amount(new Currency('GBP'), 999);

The gateway itself is not interested in how it is initialised - it is just interested in this interface
Now, I just wrote an adapter for moneyphp\money. It is very simple and could arguable have been done using injection rather than extending, but it's just a simple adapter.
That adapter now allows me to create an amount object using everything that Money\Money provides, for example:
$amount = new \Academe\SagePayMsg\Money\Money(999, new \Money\Currency('GBP'));

That offloads all the storage, conversion, validation etc. into a third-party library. I'm not locked into that library either - it took ten minutes to add this one, so could add other quickly too. For a gateway to be usable in many applications and frameworks, it is important to be able to do this - to have options, maybe with a simple built-in solution, but ultimately interfaces that make it easier to plug in alternatives.
The same approach, IMO, should or could apply to other areas, such as the PSR-7 messages, address classes, basket, payment type (card, token, null etc.)

@barryvdh Providing multiple ways of instantiating a Money object is only a solution to something that is apparently a problem. I would prefer a strict format too.
And maybe it is a problem. The users of Omnipay are very broad group. People that are using Active Record are probably using the same decimal string format in their models that Omnipay is now using. Changing that to a value object might cause confusion for them. But maybe providing multiple ways of instantiating the value object helps them. You can better forsee this than I do.
The solution of @judgej is a valid alternative. In the end of the day, Omnipay is only interested in a very few ways to get the amount and currency. A contract for that by using an interface is a good solution for it.

Mentioning here as well: Money object is final as of 3.0, so writting adapters might not be so easy, unless we are talking about some kind of wrappers.

I do agree that while it may not be ideal, it would probably be more difficult to upgrade when we don't accept their stored input. But we could offer it as a deprecated option, so it can be removed in the future.
So something in the lines of https://github.com/thephpleague/omnipay-common/commit/a82d250b5da94f4814057f6c82f376354b7ca29f ?
$request = $gateway->purchase([
    'amount' => Amount::fromDecimal(5.12, 'EUR'),
]);

$request = $gateway->purchase([
    'amount' => 512,
]);

$request = $gateway->purchase([
    'amount' => ['value' => 512, 'currency' => 'EUR'],
]);

$request = $gateway->purchase([
    'amount' => new Amount(512, 'EUR'),
]);

One thing to think about is that we're now binding the amount to a currency. This is probably a good thing, but it doesn't allow to set a global currency for the gateway (like before, with default parameters), only combined with the amount. And it isn't possible to NOT set a currency, but this is perhaps also a good thing..

@barryvdh When you define an Amount object, you will have the freedom to construct it as you like: with, without or optional currency. But if the currency is defined, it is defined together with amount, which makes sense.
I think an amount object is not a bad idea. Then I would not even allow the money object. Money users like me can perfectly convert our Money objects to Amount.
$request = $gateway->purchase([
    'amount' => new Amount($price->getAmount(), $price->getCurrency()->getCode())
]);

I don't see a problem with the above.

Makes sense. I've updated to PR.
I'll see what the other say about the currency parameter.

This has been resolved in the PR @barryvdh and it looks like there's agreement here. Closing this issue.

Also related: http://verraes.net/2016/02/type-safety-and-money/
Where handling Bitcoin might be a valid concern for this.
Still not really sure if floats or integers should be the base input. Perhaps scan the existing gateways to see what is more common.

As an internal representation, I would stick with integer as the common representation. Can save you a lot on rounding, but it adds some complexity.


Request: Make have_a_version_with accept a RegEx

I want to check that a field is versioned, but part of the field itself is generated randomly, so I don't have control over that. I'd like to just provide a regular expression to the have_a_version_with matcher:
    it 'versions name, email, and avatar' do
      user = create :user, :donald, :with_avatar

      expect {
        user.update_attributes! avatar: dummy_file('other_image.jpg')
      }.to change { PaperTrail::Version.count(item_type: 'User') }.by 1

      expect(user).to have_a_version_with avatar: /-image\.jpg$/ # The avatar's name is something like<PHONE_NUMBER>-13764-8321-image.jpg
    end

have_a_version_with calls PaperTrail::Version#where_object, which should return positive matches for partial names just like a Regexp (at least it definitely can if you control the beginning portion of the name as opposed to the end).
I don't believe you can do Regexp matching inside of all SQL databases, thus ActiveRecord doesn't support querying with Regexp's as a value matcher to my knowledge.  Due to that you may be better off querying your versions table with a LIKE query or something to that effect. This StackOverflow description seems to be more knowledgable of how to accomplish this than I am, so it may help you.
It sounds like MySQL and PostgreSQL both have partial support for Regexp matching / querying but they are inconsistent and I don't believe SQLite or other SQL-based databases all support them, thus I don't believe there is an easy way to have a standard accepted Regexp matcher for something like you're asking.
If you or anyone else know more about this than me and I'm wrong I'd love to see a pull request with the code necessary for this.

Joshua, as a workaround you could reify.  This would be too slow for production code, but it should be fine in a test.
expect(user.versions.any? { |v| v.reify.avatar ~ /-image\.jpg$/}).to eq(true)

Postgres has support for regex matching inside a json column.
select * from versions where object->>'avatar' ~ '-image\.jpg$';

However, arel support for json columns is still being worked on.  More importantly, as Ben said, MySQL doesn't have a json type, and SQLite does not even support regex by default.

.. I'd love to see a pull request with the code necessary for this.

I agree with Ben, I like this feature request, but I'm going to close it for now, and hope that support for this type of query improves in the future.


Create CompatSlotItemHandler

I'd like to see a new class, mcjty.lib.compat.CompatSlotItemHandler, which extends net.minecraftforge.items.SlotItemHandler, and implements getX, getY, setX, setY, onTake, and onPickup for it.
I'm working with CompatLayer, and attempting to reimplement my class extending SlotItemHandler. However, I use onTake (which is not a method in 1.10.2) and to use CompatSlot I would have to re-implement all the logic in Forge's SlotItemHandler.

I can't find an onTake() in 1.11 either? What onTake do you mean?

net.minecraft.inventory.Slot.onTake(EntityPlayer thePlayer, ItemStack stack)
I am on Forge 1.11.2-13-20.0.2252.

Sorry for the delay. Will try to deal with this later today if I find the time

Ok, working on this. Note that only onTake is needed as CompatSlot.getX also works on SlotItemHandler

Fixed next release (hopefully later)


Windows version doesn't update online status

Windows client reports that I'm away even when I'm at computer and when I have focus on JabbR.Deskopt app.

The online status only gets changed when you start typing, which is by design.  A user's online status is also controlled by the server, not the app so the only way to update ones online status would be to fake the user typing, which might not be the best way to go.

The online status only gets changed when you start typing, which is by design.  A user's online status is also controlled by the server, not the app so the only way to update ones online status would be to fake the user typing, which might not be the best way to go.

In web version user only has to focus on JabbR tab and doesn't have to type. Maybe protocol added this possibility.

In web version user only has to focus on JabbR tab and doesn't have to type. Maybe protocol added this possibility.

Ah, interesting!  I'll take a look when I can.  I haven't looked at the code in a while but I hope to give some time to it soon.
Thanks again for your bug reports!

Ah, interesting!  I'll take a look when I can.  I haven't looked at the code in a while but I hope to give some time to it soon.
Thanks again for your bug reports!


Confirm when the browser window is closed and there is unsaved work

browser confirmation on page exit
prompt to save a document when closed will only trigger when unsaved

Closes #267

This change is

Looks great!


[GSSoC'23] Time Watcher Project Completed

Title and Issue number

Title - Time Watcher
Issue Number - 2748
Close #2748

Checklist:

[x] I have mentioned the issue number in my Pull Request.
[x] I have commented my code, particularly in hard-to-understand areas
[x] I have created a helpful and easy to understand README.md

@pranjay-poddar @MohitGupta121 @Saurabh-kumar09 Sirs, please review this pr and requested to merge this pr under GSSoC'23. Sir, i requested to add a label-3 on this project.
Thanks for assigning this project to me.


Missing device configuration:

Checklist

[X] It is not in the configuration DB

[X] It was not merged recently or has a pending PR

Which device is missing?
Yale Assure Lock 2
Manufacturer ID
297
Product Type
33028
Product ID
0x8104
Firmware Version
1.78
Is the device listed on the Z-Wave Alliance website?
YES
Do you have a manual?
400 series Keyed.pdf

Can you give all the ids in their 0x... form and share the link to the entry on the Alliance website?

@AlCalzone , where can I find the 0x codes in zwave js ui?   All I was able to find was what I provided.
Here is a link to the product.  Note, there are several variations (Keyed, not keyed, Touchpad, button)
https://products.z-wavealliance.org/products/4651?selectedFrequencyId=2
Variations:
https://products.z-wavealliance.org/Search/Index?regionId=2&searchText=assure+2

where can I find the 0x codes in zwave js ui?

When you open the drop down panel for the node, it should be right at the start, sth. like 0x1234-0x2345-0xabcd

297-1490-33028 (0x0129-0x8104-0x05d2


"http/browser_client.dart" import causes tests to fail because of "http-1.2.2/lib/src/browser_client.dart:6:8: Error: Dart library 'dart:js_interop' is not available on this platform."

Dart version and tooling diagnostic info (`dart info`)
~/repositories/http_js_interop_bug_dart$ dart info
If providing this information as part of reporting a bug, please review the information
below to ensure it only contains things you're comfortable posting publicly.
General info

Dart 3.4.0 (stable) (Mon May 6 07:59:58 2024 -0700) on "macos_arm64"
on macos / Version 14.5 (Build 23F79)
locale is en-PT

Project info

sdk constraint: '^3.4.0'
dependencies: http
dev_dependencies: lints, test

Whether you are using Windows, macOS, or Linux (if applicable)

macOS

Whether you are using Chrome, Safari, Firefox, Edge (if applicable)

N/A
Steps to reproduce:

Create sample dart project dart create sample
Add http dependency to pubspec.yaml
Add an import to import 'package:http/browser_client.dart' in your main.dart
Run dart test

Expected results:
Tests should run fine and not fail because of the import
Actual results:
Tests fail because of Error: Dart library 'dart:js_interop' is not available on this platform."
Partial error logs (too big to paste all here)~/repositories/http_js_interop_bug_dart$ dart test
Resolving dependencies in `/Users/ricardodurao/repositories/http_js_interop_bug_dart`...
Downloading packages...
Got dependencies in `/Users/ricardodurao/repositories/http_js_interop_bug_dart`.
Building package executable... (2.1s)
Built test:test.
00:00 +0 -1: loading test/http_js_interop_bug_dart_test.dart [E]
  Failed to load "test/http_js_interop_bug_dart_test.dart":
  ../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/http-1.2.2/lib/src/browser_client.dart:6:8: Error: Dart library 'dart:js_interop' is not available on this platform.
  import 'dart:js_interop';
         ^
  Context: The unavailable library 'dart:js_interop' is imported through these packages:
  /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart => package:http => dart:js_interop
  /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart => package:http => package:web => dart:js_interop
  ...

Detailed import paths for (some of) the these imports:
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/battery_status.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/clipboard_apis.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/clipboard_apis.dart => package:web/src/dom/fileapi.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/clipboard_apis.dart => package:web/src/dom/fileapi.dart => package:web/src/dom/streams.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/clipboard_apis.dart => package:web/src/dom/fileapi.dart => package:web/src/dom/streams.dart => package:web/src/dom/webidl.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/clipboard_apis.dart => package:web/src/dom/fileapi.dart => package:web/src/dom/webidl.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/digital_identities.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/fedcm.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/web_otp.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/webauthn.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/webauthn.dart => package:web/src/dom/fido.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/webauthn.dart => package:web/src/dom/secure_payment_confirmation.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/webauthn.dart => package:web/src/dom/secure_payment_confirmation.dart => package:web/src/dom/payment_request.dart => dart:js_interop
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/dom.dart => package:web/src/dom/accelerometer.dart => package:web/src/dom/generic_sensor.dart => package:web/src/dom/dom.dart => package:web/src/dom/css_font_loading.dart => package:web/src/dom/html.dart => package:web/src/dom/credential_management.dart => package:web/src/dom/webauthn.dart => package:web/src/dom/webidl.dart => dart:js_interop
  ...

../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/helpers.dart:25:8: Error: Dart library 'dart:js_interop_unsafe' is not available on this platform.
import 'dart:js_interop_unsafe';
^
Context: The unavailable library 'dart:js_interop_unsafe' is imported through these packages:
  /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart => package:http => package:web => dart:js_interop_unsafe

Detailed import paths for (some of) the these imports:
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/helpers.dart => package:web/src/helpers/events/events.dart => package:web/src/helpers/events/providers.dart => dart:js_interop_unsafe
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/helpers.dart => package:web/src/helpers/events/providers.dart => dart:js_interop_unsafe
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/helpers.dart => package:web/src/helpers/http.dart => package:web/src/helpers/events/events.dart => package:web/src/helpers/events/providers.dart => dart:js_interop_unsafe
  test.dart => /Users/ricardodurao/repositories/http_js_interop_bug_dart/test/http_js_interop_bug_dart_test.dart => package:http_js_interop_bug_dart/http_js_interop_bug_dart.dart => package:http/browser_client.dart => package:http/src/browser_client.dart => package:web/web.dart => package:web/src/helpers.dart => dart:js_interop_unsafe

../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/helpers.dart:24:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/accelerometer.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/angle_instanced_arrays.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/attribution_reporting_api.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/background_sync.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/battery_status.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/clipboard_apis.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/compression.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/console.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/cookie_store.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/credential_management.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/csp.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/css_animations.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/css_animations_2.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/css_cascade.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/css_cascade_6.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^
../../.pub-cache/hosted/pvotaltech.jfrog.io%2547artifactory%2547api%2547pub%2547pvotal-pub%2547/web-1.0.0/lib/src/dom/css_conditional.dart:14:8: Error: Dart library 'dart:js_interop' is not available on this platform.
import 'dart:js_interop';
^

This is working as intended: browser_client naturally contains an HTTP client which can only run in the browser. Which means you need to run your tests in the brower. See package:test documenation on how to restrict tests to specific platforms: https://pub.dev/packages/test#restricting-tests-to-certain-platforms

This is working as intended: browser_client naturally contains an HTTP client which can only run in the browser. Which means you need to run your tests in the brower. See package:test documenation on how to restrict tests to specific platforms: https://pub.dev/packages/test#restricting-tests-to-certain-platforms

I'd suggest to add this to the error logs somehow. It is nearly impossible to figure that out by reading the logs that are presented to the dev.


NavBar left textButton is rendered incorrectly when going forth and back between views (iOS)

Setup

WebView1 - NavBar buttons are updated on visible event (left text button)
WebView2 - NavBar buttons are updated on visible event (left text button + right text button)

When using the iOS "swype left to right" as a shortcut for going back but not swyping it through but still staying in the same view and then going back causes the Nav Bar to render the left text button incorrectly. It's rendered twice and it's on top of the back indicator.
This description might not be easy to follow, so might get better idea when watching this short video: https://www.dropbox.com/s/36of1kfautik77t/VID_20141204_122238953.mp4?dl=0
This was with latest v.4.0.0 steroids and the latest scanner in iOS appstore. I'm not able to reproduce this with Android. I didn't have yet to check if this is valid for supersonic as well.

This is still valid, I'll try to create a repro case when I have time. In general I think this is the flow it requires

update nav bar in the visible event of the view pushed to stack. If you don't update the nav bar, there are no issues.


Request to join pattern house

I would like to join the organisation.
This will help me to contribute in the filed of open source and learn new things.
looking forward for a new opportunity.
thank you.

Hi! I've sent the invite, Welcome to PatternHouse!


fix undefined auth exception when object is not defined

Stack:

#16
#15
#14
#13
#12 ⬅

⚠ Part of a stack created by spr. Do not merge manually using the UI - doing so may have unexpected results.

✓ Commit merged in pull request #16


enable duplicate test names and preserve test ordering

Preserve test ordering when a plan is enabled by enumerating test names (regexp patterns).
E.g.
summary:
  Tests used by Packit/TFT CI on Github
discover:
  how: fmf
  test:
   - /setup/configure_tpm_emulator
   - /setup/install_upstream_keylime
   - "/functional/.*"

execute:
    how: tmt

As a consequence duplicate tests are enabled within a test plan.
This should address
https://github.com/psss/tmt/issues/941

@psss @lukaszachy please review

Also, please update the commit message according to the recommendations. Thanks.

what a cool feature!


inventory: Remove two OSUOSL Ubuntu 16.04 hosts

Signed-off-by: Stewart X Addison<EMAIL_ADDRESS>
Checklist

[x] commit message has one of the standard prefixes
[ ] FAQ.md updated if appropriate
[ ] other documentation is changed or added (if applicable)
[ ] playbook changes run through VPC or QPC (if you have access)
[x] for inventory.yml changes, bastillion/nagios/jenkins updated accordingly

This is being done as part of #2156 (I know that PR is newer, but I suppose it'd better to do inventory changes in one big swoop)

Doesn't make a lot of difference. These ones had been removed a while ago so was a differnet batch


Aria2 explorer icon in the task bar on Windows looks like a default document

I would love the icon in the Windows taskbar to look more like the Aria2 icon that the chrome extension uses.

Chrome issue. Can not handle by extension.


DevOps Hack v1

Sorry @shawnweisfeld ... you can't take 008, it was skipped over on purpose because it's author is still unable to PR back to me.
Can you please change yours to use 010?

Looks great @shawnweisfeld ... thanks for making the change.


Polish watcher

adds more markdown descriptions to watcher notebook and adds simple watcher test.
this is in a new PR since the other one is getting bulky.

alternatively, a version of the watcher for anyone from Warsaw

there are a lot of extraneous print's in the watcher script.  I think some of them could stay in, since it's otherwise pretty hard to follow the watcher's progress.  maybe if we really want some polish, we could add a progress bar in the future.  open to ideas and thoughts on the superfluity of print statements


Wrong path in req.url in req.on('close', ... )

Example code
router.use((req, res, next) => {
    console.log(req.url);
    req.on('close', () => {
        console.log(req.url);
    });
    next();
});

What i getting:
/ping/
/

What i expect:
/ping/
/ping/

I using express in version 4.17.1

Hello, and sorry you are having difficulties. I looked at your example but placing it into a simple app to run produces the output you are expecting. Can you provide an app we can run that produces the unexpected output? We can then help determine the reason.
const express = require('express')

const app = express()
const router = express.Router()

router.use((req, res, next) => {
  console.log(req.url)
  req.on('close', () => {
    console.log(req.url)
  })
  next()
})

app.use(router)

app.listen(3000)


Add annotations to Saved Places

Latest build can be tested here:
https://schmidtk.github.io/annotations/
This adds map annotations to Saved Places. Currently supported:

Create annotations and persist across sessions.
Sync between 2D/3D.
Annotations are included in screen capture (screenshots and recordings).
KML BalloonStyle rendered as an annotation.
Annotations exported to KML BalloonStyle for display in Google Earth.

General OpenSphere changes:

plugin.cesium.CesiumRenderer#getCoordinateFromPixel now picks the globe instead of the ellipsoid. This provides a more accurate coordinate (with altitude) when terrain is enabled.
os.webgl.IWebGLRenderer#getPixelFromCoordinate now takes an optional param to test if the coordinate is in view. If not, null is returned. This has been implemented in the Cesium renderer.
plugin.capture.MapOverlayRenderer now renders ol.Overlay instances. The original class has been renamed to plugin.capture.MapOverviewRenderer, to align with OpenLayers terms.

TODO:

[x] Merge #315 and display annotations for KML balloons.
[x] Fix 3D sync when annotations are created outside the current view.
[x] Export annotations to KML for display in Google Earth.

Just a few things from testing (I was looking at https://schmidtk.github.io/annotations/):

The pointer does not work well from the sides.

I'm not a fan of how the edit button floats outside the "window". It is fine if it disappears when the mouse is not over it, but I would rather have it show on the right side of the title bar so that it is easier to see (this goes for the Legend too, but that's separate work).
Using enough text to force a vertical scroll for a given window size caused this:

These things eat my mousewheel while zooming around. It's the inverse of a zoomable map on a scrollable page. Can we have them ignore the events unless they are focused?

#1, 3, 5 have been fixed/deployed.
For #2, moving the edit button inside the annotation causes further problems. It can't go in the header because that can be disabled, and it's difficult to display well inside the body due to text truncate/wrapping constraints with the embedded simplemde directive that renders the description text. Skipping this one, at least for now.
#4 is going to be problematic as well. Passing mouse events through to the canvas requires setting pointer-events: none on the overlay, which also prevents attaching a click/mousedown handler to remove that and "focus" the overlay to allow scrolling its contents.


Add Search File Name to Search capability

I'd like the search feature to include the file/folder names in what is is searching for as well as the contents of the files. Frequently in a complex file navigating the tree structure is painful, and it'd be great to be able to jump to exactly the file I'm looking for, rather than expanding the 12th level of directory and scrolling through every small file.

This sounds like what ctrl/cmd+P does, have you seen that?

@roblourens This works for simpler use cases, but if I want to bring regex to the search, this doesn't seem to be an option. If I could apply regex in that search feature I think that would address the issue I raised on stack overflow last year.

I see, thanks. I don't plan to support this through the vscode UI, but there is an extension API, findFiles, which lets you search files by glob pattern, if that helps.

Please rethink that. We got regex for text inside files but not for the file names itself... Ehhhh. Doesn't seem consistent.

I'd like to +1 on this as there is the File Explorer filter available however it has a major problem is that it only search the visible files which is useless when you have a big tree.

You can type to filter the currently visible files in the File Explorer.

I would also like the feature to search for files by regex. For my particular use case, I would like this to be part of the API (and this would probably also involve implementing it in the user interface).

Hello, no idea why this gets constantly refused as it seems... This is a basic feature of modern IDEs I would think. Even simplier ones like Notepad++ provide more comfortable ways to e.g. regex search both file names and contents.
There are popular requests/confusion on the web, also, for example;
https://superuser.com/questions/1493167/vs-code-search-in-both-file-names-and-file-contents
I saw in another issue that you want to keep search interface simplistic. That's fine. But it could be another command and IMO it's something that should 100% be in-built and not a plugin.

It is 2023 ... why is this feature not built-into VSCode yet?

+1 on this, such a basic feature. Would've loved this, or an easy button towards @KyrietS 's response


Missing Methods Since Code Clean Up

Some methods went missing from 2.0 onwards but their removal was not documented in the release notes. World.GetGroundZ is at least one example. Its removal broke mods like Just Cause 2 and Shark-O-Matic. I am not the author of those mods, but I could reach out to them if there was a replacement API to use. Just browsing the code, though, I'm not clear what the replacement is for that API.

World.GetGroundZ never got removed, it was just renamed to World.GetGroundHeigth in 1371e63bd7a4b7678a3586050961e5d658f76f09 (line)


Migrate CI system from travis-ci.org

Hey @cantino, travis-ci is updating their CI plans, and removing the free unlimited open source plan.  Instead, they'll have a free plan that has limited credits which are not replenished.

Free Plan #
A ‘Free’ Plan, assigned automatically to every new sign up, is a Usage based plan without limits on users and comes with a trial pool of credits to be used. Once these credits are used they are not replenished. Request OSS Credits allowance or please consider one of our available plans.

Github Actions offers free CI for public repos, so I was thinking we should migrate reckon to use that.
I think I have permissions to setup github actions, but wanted to get your thoughts before setting that up.
Thanks

Blog post on someone else who migrated from Travis to Github Actions:

https://www.jeffgeerling.com/blog/2020/travis-cis-new-pricing-plan-threw-wrench-my-open-source-works

Blog post on someone else who migrated from Travis to Github Actions:

https://www.jeffgeerling.com/blog/2020/travis-cis-new-pricing-plan-threw-wrench-my-open-source-works

I'm fine if you want to migrate the project— you're very much the maintainer at this point (and doing a great job!). Is there anything I need to do to make sure you can move forward?

I'm fine if you want to migrate the project— you're very much the maintainer at this point (and doing a great job!). Is there anything I need to do to make sure you can move forward?


[BUG] Translation (manage storage / service -> compute offering)

Describe the bug
Translation in Compute Offering is Missing.
To Reproduce
Steps to reproduce the behavior:

Compute -> Compute Offerings
Add a new Offer
"compute offering type" needs to be translated, "customconstrained", "customunconstrained"
and
select "QoS Type" -> Storage -> "iscustomizeddiskiops", "diskiopsmin", "diskopsmax"
and fixed in PR #315
select "QoS Type" -> Hypervisor -> "diskbytesreadrate", "diskbyteswriterate", "diskiopsreadrate", "diskiopswriterate"

Expected behavior
A clear and concise description of what you expected to happen.
Screenshots

@utchoang does it fit?


Fix Apolcalyptic Maelstrom Desc. and Accuracy

make it so it says it does damage and that it's 100% accurate

I also fixed the z move and the fact it could not be protected or was mirrorable.